文件断点续传
最近因为开发任务,涉及到一个文件断点续传,看了一下重点就是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 {
@Override
public 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 {
//获取一个socket
serverSocket = 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{
@Override
public 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
- 临时文件,传输内容就写在这个临时文件内,下一次继续传输时相当于继续在这个临时文件内进行内容追加
- 若是没有指定续传的文件路径,则会再当前项目的根目录下;