Java-Socket實現文件的斷點續傳

        前段時間由於任務須要本人這個java渣渣開始研究如何用java實現簡單的文件斷點續傳。所謂的文件斷點續傳,個人理解是文件在傳輸過程當中由於某些緣由程序中止運行文件終止傳輸,下一次從新傳輸文件的時候還能從上一次傳輸的位置開始傳輸,而不須要從新從頭開始。java

文件傳輸的過程分爲發送方和接收方,最終個人思路是這樣的:dom

     1:傳輸開始以前發送方先向接收方發送一個確認信息,而後再向接收方發送準備發送的文件的文件名
     2:接收方收到確認信息以後,接收從發送方發送過來的文件名,接收完以後向發送方發送一個確認信息表示文件名接收完畢,而後接收方根據收到的文件名建立一個「.temp」File對象和一個「.temp」RandomAccessFile對象。獲取這個File對象所對應文件的長度(大小)(這個長度就是接收方已經接受的長度,若是以前沒有接收過這個文件,長度就爲0),並把文件長度發送給發送方。
     3:發送方收到確認信息以後,接收接受方發送的文件長度,而後向接收方發送準備發送的文件的總長度,並向接收方發送一個確認信息。而後根據接收方發送的文件長度,從文件對應長度的位置開始發送。
     4:接收方收到確認信息以後,接受發送方發送過來的數據,而後今後文件的末尾寫入。接受完成以後再將「.temp」文件重命名爲正常的文件名。

socket

把過程畫成圖就是下面這樣:測試

ok」表示確認信息
 
可以實現斷點續傳的關鍵就是使用了RandomAccessFile,此類的實例支持對隨機訪問文件的讀取和寫入。
 
加入一些如進度條、文件選擇器之類的GUI,最終的主要代碼以下:
發送方代碼:

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 SendFile 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 SendFile(){
    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);

          //傳輸界面
         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) {

   }
   }
 }

}spa

接收方代碼:

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 ReceiveFile 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 ReceiveFile(){
     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("FileReceive 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) {

    }
   }
  }

}.net

接收方測試:

public class FileReceiveTest{//接收方

  public static void main(String[] args) {
    // TODO Auto-generated method stub 
    ReceiveFile rf=new ReceiveFile();
    rf.start();
  }

}3d

發送方測試:

public class FileSendTest{//發送方

  public static void main(String[] args) {
    // TODO Auto-generated method stub
    SendFile sf=new SendFile();
    sf.start();
  }

}code

  注意 先運行接收方代碼再運行發送方代碼,測試的時候咱們選一個大一點的文件,我這裏選了個電影文件,運行結果以下:
  首先會有是否接收的提示框

 

點擊是後,開始接收,點擊否就取消orm

 

至此就成功結束了!對象

相關文章
相關標籤/搜索