文件断点续传
最近因为开发任务,涉及到一个文件断点续传,看了一下重点就是RandomAccessFile类
RandomAccessFile :
随机存取文件。一个随机访问文件的行为就像一个大的存储在文件系统中的字节数组。有一种光标,或索引进入隐含数组,称为文件指针;输入操作从文件指针开始读取字节并前进文件指针超过读取的字节。如果随机访问文件是以读/写模式创建,输出操作也可用;输出操作从文件指针开始写入字节并前进文件指针超过所写的字节。输出操作写超过隐含数组的当前末尾导致数组为扩展
我用GUI搞了个界面,看着更直观一点,不多说上代码,一个简单的Demo
发送文件:
/*** 断点续传 ----发送方*/
public class DDXCSendFile extends Thread {private Socket socket = null;//输入输出字节流private DataOutputStream dataOutputStream;private DataInputStream dataInputStream;//随意读取文件类private RandomAccessFile randomAccessFile;//搞一个GUI界面private JFrame jFrame;private Container container;private JLabel jLabel;private JProgressBar jProgressBar;public DDXCSendFile() {jFrame = new JFrame("oO=断点续传=Oo");try {socket = new Socket("localhost", 8080);} catch (IOException e) {e.printStackTrace();}}//上传方法public void run() {//文件选择器JFileChooser jFileChooser = new JFileChooser();int dialog = jFileChooser.showOpenDialog(null);if (dialog == JFileChooser.APPROVE_OPTION) {String path = jFileChooser.getSelectedFile().getPath();try {dataOutputStream = new DataOutputStream(socket.getOutputStream());dataInputStream = new DataInputStream(socket.getInputStream());dataOutputStream.writeUTF("ok");//读取这个文件,权限只是读取randomAccessFile = new RandomAccessFile(path, "r");File file = new File(path);byte[] bytes = new byte[1024];dataOutputStream.writeUTF(file.getName());dataOutputStream.flush();String readUTF = dataInputStream.readUTF();if (readUTF.equals("ok")) {//已发送大小long readLong = dataInputStream.readLong();dataOutputStream.writeLong(randomAccessFile.length());dataOutputStream.writeUTF("ok");dataOutputStream.flush();//偏移量--及发送了多少long offset = readLong;int sendSize = (int) (randomAccessFile.length() / 1024);int sendOffset = (int) (offset / 1024);//发送窗口jFrame.setSize(300, 300);//内容面板container = jFrame.getContentPane();//设置布局container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));//进度条jProgressBar = new JProgressBar();jLabel = new JLabel(file.getName() + " 发送中:");container.add(jLabel);jProgressBar.setOrientation(JProgressBar.HORIZONTAL);jProgressBar.setMinimum(0);jProgressBar.setMaximum(sendSize);jProgressBar.setValue(sendOffset);jProgressBar.setStringPainted(true);jProgressBar.setBorderPainted(true);jProgressBar.setBackground(Color.GREEN);jProgressBar.setPreferredSize(new Dimension(260, 50));//按钮JButton jButton = new JButton("取消");//画板JPanel jPanel = new JPanel();jPanel.setLayout(new FlowLayout(FlowLayout.LEFT));jPanel.add(jProgressBar);jPanel.add(jButton);container.add(jPanel);//取消按钮添加监听器jButton.addActionListener(new CancelActionListener());jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);jFrame.setVisible(true);//读取的位置int length;if (offset < randomAccessFile.length()) {randomAccessFile.seek(offset);while ((length = randomAccessFile.read(bytes)) > 0) {dataOutputStream.write(bytes, 0, length);jProgressBar.setValue(++sendOffset);dataOutputStream.flush();}}jLabel.setText(file.getName() + " 发送完成");}dataOutputStream.close();dataInputStream.close();randomAccessFile.close();} catch (IOException e) {e.printStackTrace();jLabel.setText("取消发送,连接关闭");} finally {jFrame.dispose();}}}class CancelActionListener implements ActionListener {@Overridepublic void actionPerformed(ActionEvent e) {try {jLabel.setText("取消发送,连接关闭");JOptionPane.showMessageDialog(jFrame, "取消发送,连接关闭!", "提示:", JOptionPane.INFORMATION_MESSAGE);dataInputStream.close();dataOutputStream.close();randomAccessFile.close();jFrame.dispose();socket.close();} catch (IOException ex) {ex.printStackTrace();}}}
}
接收文件:
/*** 断点续传 ----接收方*/
public class DDXCRecevieFile extends Thread {private Socket socket = null;private ServerSocket serverSocket = null;//输入输出字节流private DataOutputStream dataOutputStream;private DataInputStream dataInputStream;//随意读取文件类private RandomAccessFile randomAccessFile;//搞一个GUI界面private JFrame jFrame;private Container container;private JLabel jLabel;private JProgressBar jProgressBar;public DDXCRecevieFile(){jFrame = new JFrame("oO=接收文件=Oo");try {//获取一个socketserverSocket = new ServerSocket(8080);socket = serverSocket.accept();} catch (IOException e) {e.printStackTrace();}}//接收方法public void run(){try {dataOutputStream = new DataOutputStream(socket.getOutputStream());dataInputStream = new DataInputStream(socket.getInputStream());dataInputStream.readUTF();int dialog = JOptionPane.showConfirmDialog(jFrame, "是否接收文件?", "文件发送请求:", JOptionPane.YES_NO_OPTION);if (dialog==JOptionPane.YES_OPTION){//表示同意接收String filename = dataInputStream.readUTF();dataOutputStream.writeUTF("ok");dataOutputStream.flush();File file = new File(filename + ".temp");//任意读取文件对象,权限为可读可写randomAccessFile = new RandomAccessFile(filename+".temp","rw");//文件大小long fileSize = 0;if (file.exists() && file.isFile()){fileSize = file.length();}//将接收的写入磁盘dataOutputStream.writeLong(fileSize);dataOutputStream.flush();//读取的文件大小long allSize = dataInputStream.readLong();String readUTF = dataInputStream.readUTF();int barSize = (int)(allSize/1024);int barOffset = (int)(fileSize/1024);//发送文件传输界面jFrame.setSize(300,300);//内容面板container = jFrame.getContentPane();//设置布局container.setLayout(new BoxLayout(container,BoxLayout.Y_AXIS));//进度条jProgressBar = new JProgressBar();jLabel = new JLabel(file.getName()+" 接收中:");container.add(jLabel);jProgressBar.setOrientation(JProgressBar.HORIZONTAL);jProgressBar.setMinimum(0);jProgressBar.setMaximum(barSize);jProgressBar.setValue(barOffset);jProgressBar.setStringPainted(true);jProgressBar.setBorderPainted(true);jProgressBar.setBackground(Color.GREEN);jProgressBar.setPreferredSize(new Dimension(260,50));//按钮JButton jButton = new JButton("取消");//画板JPanel jPanel = new JPanel();jPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));jPanel.add(jProgressBar);jPanel.add(jButton);container.add(jPanel);//取消按钮添加监听器jButton.addActionListener(new CancelActionListener());jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);jFrame.setVisible(true);if (readUTF.equals("ok")){randomAccessFile.seek(fileSize);int length;byte[] bytes = new byte[1024];while ((length=dataInputStream.read(bytes,0,bytes.length))!=-1){randomAccessFile.write(bytes,0,length);jProgressBar.setValue(++barOffset);}System.out.println(filename+" 接收完成!");}jLabel.setText(filename+" 接收完成!");//关流dataInputStream.close();dataOutputStream.close();randomAccessFile.close();jFrame.dispose();//重命名文件if (barOffset>=barSize){file.renameTo(new File(filename));}}else {dataInputStream.close();dataOutputStream.close();jFrame.dispose();}} catch (IOException e) {e.printStackTrace();jLabel.setText("已取消接收,连接关闭!");}finally {jFrame.dispose();}}class CancelActionListener implements ActionListener{@Overridepublic void actionPerformed(ActionEvent e) {try {dataInputStream.close();dataOutputStream.close();randomAccessFile.close();JOptionPane.showMessageDialog(jFrame,"已取消接收,连接关闭!","提示:",JOptionPane.INFORMATION_MESSAGE);jLabel.setText("已取消接收,连接关闭!");} catch (IOException ex) {ex.printStackTrace();}}}
}
测试代码:
//接收方先启动
public class DDXCFileRecevieTest {public static void main(String[] args) {DDXCRecevieFile ddxcreceiveFile = new DDXCRecevieFile();ddxcreceiveFile.start();}
}//发送方后启动
public class DDXCFileSendTest {public static void main(String[] args) {DDXCSendFile ddxcSendFile = new DDXCSendFile();ddxcSendFile.start();}
}
执行效果图:
开始发送
断开发送
断开之后,项目根路径下,有一个.temp临时文件,传输的内容就在这个文件内,后面续传类似于内容追加进该文件
再次启动续传该文件,会从之前取消时的 31% 进行续传
传输完成
备注:
- 先执行接收测试类,再执行发送测试类; 续传时会生成一个 .temp
- 临时文件,传输内容就写在这个临时文件内,下一次继续传输时相当于继续在这个临时文件内进行内容追加
- 若是没有指定续传的文件路径,则会再当前项目的根目录下;