断点续传
什么是断点续传?
断点续传其实正如字面意思,就是在上传或下载的断开点继续开始传输,不用再从头开始。
断点的由来是在下载过程中,将一个下载文件分成了多个部分,同时进行多个部分一起的下载,当某个时间点,任务被暂停了,此时下载暂停的位置就是断点了。
续传就是当一个未完成的下载任务再次开始时,会从上次的断点继续传送。
这样的功能如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载以后未上传下载的部分,而没有必要重头开始上传下载。可以节省时间,提高速度。
实现过程
1.传输开始之前发送方先向接收方发送一个确认信息,然后再向接收方发送准备发送的文件的文件名
2.接收方收到确认信息之后,接收从发送方发送过来的文件名,接收完之后向发送方发送一个确认信息表示文件名接收完毕,然后接收方根据收到的文件名创建一个“.temp”File对象和一个“.temp”RandomAccessFile对象。获取这个File对象所对应文件的长度(大小)(这个长度就是接收方已经接受的长度,如果之前没有接收过这个文件,长度就为0),并把文件长度发送给发送方。
3.发送方收到确认信息之后,接收接受方发送的文件长度,然后向接收方发送准备发送的文件的总长度,并向接收方发送一个确认信息。然后根据接收方发送的文件长度,从文件对应长度的位置开始发送。
4.接收方收到确认信息之后,接受发送方发送过来的数据,然后从此文件的末尾写入。接受完成之后再将“.temp”文件重命名为正常的文件名。
示意图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zjPIQNfy-1604366610712)(C:\Users\Administrator\Desktop\断点续传\20180120013406658.jpg)]
“ok”表示确认信息.
实现代码
1.发送端
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.Socket;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class SendFileThread extends Thread{
private Socket socket=null;
private DataOutputStream dos;
private DataInputStream dis;
private RandomAccessFile rad;
private Container contentPanel;
private JFrame frame;
private JProgressBar progressbar;
private JLabel label;
public SendFileThread(){
frame=new JFrame("文件传输");
try {
socket=new Socket("localhost", 8080);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run(){
JFileChooser fc = new JFileChooser();
int status=fc.showOpenDialog(null);
if (status==JFileChooser.APPROVE_OPTION) {
String path=fc.getSelectedFile().getPath();
try {
dos=new DataOutputStream(socket.getOutputStream());
dis=new DataInputStream(socket.getInputStream());
dos.writeUTF("ok");
rad=new RandomAccessFile(path, "r");
File file=new File(path);
byte[] buf=new byte[1024];
dos.writeUTF(file.getName());
dos.flush();
String rsp=dis.readUTF();
if (rsp.equals("ok")) {
long size=dis.readLong();//读取文件已发送的大小
dos.writeLong(rad.length());
dos.writeUTF("ok");
dos.flush();
long offset=size;//字节偏移量
int barSize=(int) (rad.length()/1024);
int barOffset=(int)(offset/1024);
2.发送端图形界面
//传输界面
frame.setSize(380,120);
contentPanel = frame.getContentPane();
contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
progressbar = new JProgressBar();//进度条
label=new JLabel(file.getName()+" 发送中");
contentPanel.add(label);
progressbar.setOrientation(JProgressBar.HORIZONTAL);
progressbar.setMinimum(0);
progressbar.setMaximum(barSize);
progressbar.setValue(barOffset);
progressbar.setStringPainted(true);
progressbar.setPreferredSize(new Dimension(150, 20));
progressbar.setBorderPainted(true);
progressbar.setBackground(Color.pink);
JButton cancel=new JButton("取消");
JPanel barPanel=new JPanel();
barPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
barPanel.add(progressbar);
barPanel.add(cancel);
contentPanel.add(barPanel);
cancel.addActionListener(new cancelActionListener());
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//从文件指定位置开始传输
int length;
if (offset<rad.length()) {
rad.seek(offset);
while((length=rad.read(buf))>0){
dos.write(buf,0,length);
progressbar.setValue(++barOffset);
dos.flush();
}
}
label.setText(file.getName()+" 发送完成");
}
dis.close();
dos.close();
rad.close();
} catch (IOException e) {
// TODO Auto-generated catch block
label.setText(" 取消发送,连接关闭");
}finally {
frame.dispose();
}
}
}
class cancelActionListener implements ActionListener{
public void actionPerformed(ActionEvent e3){
try {
label.setText(" 取消发送,连接关闭");
JOptionPane.showMessageDialog(frame, "取消发送给,连接关闭!", "提示:", JOptionPane.INFORMATION_MESSAGE);
dis.close();
dos.close();
rad.close();
frame.dispose();
socket.close();
} catch (IOException e1) {
}
}
}
启动写一个方法调用即可
public class FileSendTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
SendFileThread sf=new SendFileThread();
sf.start();
}
}
3.接收端代码
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class ReceiveFileThread extends Thread{
private ServerSocket connectSocket=null;
private Socket socket=null;
private JFrame frame;
private Container contentPanel;
private JProgressBar progressbar;
private DataInputStream dis;
private DataOutputStream dos;
private RandomAccessFile rad;
private JLabel label;
public ReceiveFileThread(){
frame=new JFrame("接收文件");
try {
connectSocket=new ServerSocket(8080);
socket=connectSocket.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run(){
try {
dis=new DataInputStream(socket.getInputStream());
dos=new DataOutputStream(socket.getOutputStream());
dis.readUTF();
int permit=JOptionPane.showConfirmDialog(frame, "是否接收文件","文件传输请求:", JOptionPane.YES_NO_OPTION);
if (permit==JOptionPane.YES_OPTION) {
String filename=dis.readUTF();
dos.writeUTF("ok");
dos.flush();
File file=new File(filename+".temp");
rad=new RandomAccessFile(filename+".temp", "rw");
//获得文件大小
long size=0;
if(file.exists()&&file.isFile()){
size=file.length();
}
dos.writeLong(size);//发送已接收的大小
dos.flush();
long allSize=dis.readLong();
String rsp=dis.readUTF();
int barSize=(int)(allSize/1024);
int barOffset=(int)(size/1024);
//传输界面
frame.setSize(300,120);
contentPanel =frame.getContentPane();
contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
progressbar = new JProgressBar();//进度条
label=new JLabel(filename+" 接收中");
contentPanel.add(label);
progressbar.setOrientation(JProgressBar.HORIZONTAL);
progressbar.setMinimum(0);
progressbar.setMaximum(barSize);
progressbar.setValue(barOffset);
progressbar.setStringPainted(true);
progressbar.setPreferredSize(new Dimension(150, 20));
progressbar.setBorderPainted(true);
progressbar.setBackground(Color.pink);
JButton cancel=new JButton("取消");
JPanel barPanel=new JPanel();
barPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
barPanel.add(progressbar);
barPanel.add(cancel);
contentPanel.add(barPanel);
cancel.addActionListener(new cancelActionListener());
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//接收文件
if (rsp.equals("ok")) {
rad.seek(size);
int length;
byte[] buf=new byte[1024];
while((length=dis.read(buf, 0, buf.length))!=-1){
rad.write(buf,0,length);
progressbar.setValue(++barOffset);
}
System.out.println("end");
}
label.setText(filename+" 结束接收");
dis.close();
dos.close();
rad.close();
frame.dispose();
//文件重命名
if (barOffset>=barSize) {
file.renameTo(new File(filename));
}
}else{
dis.close();
dos.close();
frame.dispose();
}
} catch (IOException e) {
// TODO Auto-generated catch block
label.setText(" 已取消接收,连接关闭!");
}finally {
frame.dispose();
}
}
class cancelActionListener implements ActionListener{
public void actionPerformed(ActionEvent e){
try {
dis.close();
dos.close();
rad.close();
JOptionPane.showMessageDialog(frame, "已取消接收,连接关闭!", "提示:", JOptionPane.INFORMATION_MESSAGE);
label.setText(" 取消接收,连接关闭");
} catch (IOException e1) {
}
}
}
}
接收端启动调用方法
public class FileReceiveTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ReceiveFileThread rf=new ReceiveFileThread();
rf.start();
}
}
RMATION_MESSAGE);
label.setText(" 取消接收,连接关闭");
} catch (IOException e1) {
}
}
}
}
接收端启动调用方法
public class FileReceiveTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ReceiveFileThread rf=new ReceiveFileThread();
rf.start();
}
}
注意一定要先启动接受方,在启动发送方