zoukankan      html  css  js  c++  java
  • 文件断点续传实现 (1 -- java实现)

    java服务端和java客户端的代码

    ( C# 客户端参考 文件断点续传实现 ( 2-- C# 客户端) )

    服务端:

    package com.sunyard.vst.upload;
    
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * @标题 Socket服务
     * @作者 gdl
     *
     */
    public class UploadServer extends Thread{
    
        private static Logger LOG = LoggerFactory.getLogger(UploadServer.class);
        
        /**
         * 上传端口
         */
        private int socketPort ;
        
        /**
         * 线程池数量
         * (不是单个CPU线程池大小,是总数)
         */
        private int poolSize ;
        
        /**
         * Socket服务
         */
        private ServerSocket serverSocket ;
        
        private boolean running = true;
        
        /**
         * 线程池
         */
        private ExecutorService executorService;
        
        public UploadServer(int socketPort,int poolSize) {
            this.socketPort = socketPort;
            this.poolSize = poolSize;
            try {
                serverSocket = new ServerSocket(socketPort);
                executorService =Executors.newFixedThreadPool(poolSize);
            }catch(Exception e) {
                LOG.error("构造函数(初始化)异常",e);
            }
    
        }
        
        public void run() {
            LOG.info("服务已经启动...");
            while(running) {
                Socket socket = null;
                try {
                    socket = serverSocket.accept();
                    executorService.execute(new UploadHandler(socket));
                }catch(Exception e) {
                    LOG.info(e.getMessage(),e);
                }
                try {
                    LOG.info("一个Socket已经连接!");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
        /**
         * 停止服务
         */
        public void Stop() {
            this.running = false; 
        }
    }

    Socket处理类

    package com.sunyard.vst.upload;
    
    import java.io.Closeable;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.RandomAccessFile;
    import java.net.Socket;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.sunyard.vst.utils.MD5Util;
    
    /**
     * @title Socket处理类
     * @author gdl
     *
     */
    public class UploadHandler implements Runnable{
    
        private static Logger LOG = LoggerFactory.getLogger(UploadHandler.class);
        
        /**
         * Socket
         */
        private Socket socket ;
        private String dir = "E:\temp\files" ;
        private int buffer_size = 1024*64 ;//每次传输文件长度
        
        private InputStream input = null;
        private OutputStream output = null;
        private DataInputStream in = null;
        private DataOutputStream out = null;
        private RandomAccessFile raf = null;
        
        public UploadHandler(Socket socket) {
            LOG.info("连接成功");
            this.socket = socket ;
        }
        
        /**
         * 1. 首先获取文件的md5,文件大小,文件名称
         * 2. 返回前端文件是否存在,文件大小,文件名
         * 
         */
        public void run() {
            try {
                input = socket.getInputStream();
                output = socket.getOutputStream();
                in = new DataInputStream(input);
                out = new DataOutputStream(output);
                
                //第一次交互
                int len = in.readInt();
                byte[] buffer = new byte[len];
                in.read(buffer);
                String msg = new String(buffer,UploadConstant.CHARSET);
                LOG.info("MSG:"+msg);
                String[] strs = msg.split("\|");
                String filemd5 = strs[0] ;
                long filelength = Long.valueOf(strs[1]);
                String filename = strs[2];
                
                msg = analyse(filemd5, filelength, filename);
                
                buffer = msg.getBytes(UploadConstant.CHARSET);
                out.writeInt(buffer.length);
                out.write(buffer);
                out.flush();
                
                
                //第二次交互 (仅当02开头时继续上传)
                if(msg.startsWith(UploadConstant.E02)) {
                    File tempfile = new File(dir,filename+".tmp");
                    if(!tempfile.exists()) {
                        tempfile.createNewFile();
                    }
                    raf=new RandomAccessFile(tempfile, "rw");
                    long offset = tempfile.length() ;
                    raf.seek(offset);
                    int length;
                    byte[] buf=new byte[buffer_size];
                    while((length=in.read(buf, 0, buf.length))!=-1){
                        raf.write(buf,0,length);
                        offset += length;
                        //响应上传进度
                        if(offset == filelength) {
                            //上传结束
                            break;
                        }
                    }
                    try {
                        close(raf);
                        raf = null;
                    }finally {
                        msg = response(filemd5, filename, tempfile);
                        
                        buffer = msg.getBytes(UploadConstant.CHARSET);
                        out.writeInt(buffer.length);
                        out.write(buffer);
                        out.flush();
                    }
                }
            } catch(Exception e) {
                LOG.error(e.getMessage(),e);
            } finally {
                close(raf);
                close();
            }
                    
        }
    
        /**
         * 上传结束后响应报文
         * @param filemd5
         * @param filename
         * @param tempfile
         * @return
         */
        private String response(String filemd5, String filename, File tempfile) {
            String msg;
            String md5 = MD5Util.getMD5(tempfile);
            if(md5.equalsIgnoreCase(filemd5)) {
                File target = new File(dir,filename);
                LOG.info("开始重命名");
                tempfile.renameTo(target);//重命名
                if(target.exists()) {
                    LOG.info("重命名成功");
                    msg = UploadConstant.E06;
                }else {
                    LOG.info("重命名失败");
                    msg = UploadConstant.E04;
                }
                
            }else {
                LOG.info("上传成功,但是MD5和上传前不同");
                msg = UploadConstant.E05;
            }
            return msg;
        }
    
        /**
         * 分析
         * @param filemd5
         * @param filelength
         * @param filename
         * @return 
         *   00 文件大小为0
         *   01 文件存在,不需要重新上传
         *   02 临时文件存在,竖线后是文件大小
         *   03 文件存在,但是md5不同
         * @throws IOException
         */
        private String analyse(String filemd5, long filelength, String filename) throws IOException {
            String msg;
            //根据md5,文件名等信息获取文件是否存在
            File file = new File(dir,filename);
            if(file.exists()) {
                String md5 = MD5Util.getMD5(file);
                if(md5.equalsIgnoreCase(filemd5)) {
                    LOG.info("上传文件已经存在且MD5相同,不需要重新上传");
                    msg = UploadConstant.E01 ;
                }else {
                    LOG.info("上传文件已经存在,但是MD5不同");
                    msg = UploadConstant.E03 ;
                }
            }else {
                if(0 == filelength) {//上传文件大小为零
                    file.createNewFile();
                    LOG.info("上传文件大小为0");
                    msg = UploadConstant.E00 ;
                }else {
                    file = new File(dir,filename+".tmp");//未上传完整的临时文件
                    if(file.exists()) {
                        long len = file.length();
                        LOG.info("上传文件不存在,但是临时文件存在,文件大小:"+len);
                        msg = UploadConstant.E02+"|"+buffer_size+"|"+len ;
                    }else {
                        LOG.info("上传文件不存在");
                        msg = UploadConstant.E02+"|"+buffer_size+"|0" ;
                    }
                }
            }
            return msg;
        }
        
        private void close() {
            close(out);
            close(in);
            close(output);
            close(input);
        }
        
        private void close(Closeable stream) {
            if(null != stream) {
                try {
                    stream.close();
                } catch (Exception e) {
                    LOG.error("关闭{}发生异常",stream.getClass().getSimpleName(),e);
                }
            }
        }
    
    }

    常量类

    package com.sunyard.vst.upload;
    
    /**
     * 
     *   00 文件大小为0
     *   01 文件存在,不需要重新上传
     *   02 临时文件存在,竖线后是文件大小
     *   03 文件存在,但是md5不同
     *   
     *   04 上传后重命名失败
     *   05 上传后MD5不一致
     *   06 上传成功,MD5一致,重命名正常
     * @author gdl
     *
     */
    public class UploadConstant {
    
        public static final String E00 = "00";//文件大小为0
        public static final String E01 = "01";//文件存在,不需要重新上传
        public static final String E02 = "02";//临时文件存在,竖线后是文件大小
        public static final String E03 = "03";//文件存在,但是md5不同
        
        public static final String E04 = "04";//上传后重命名失败
        public static final String E05 = "05";//上传后MD5不一致
        public static final String E06 = "06";//上传成功,MD5一致,重命名正常
        
        public static final String CHARSET = "gbk";//文本编码
        
    }

    工具类(获取文件MD5):

    package com.sunyard.vst.utils;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.security.MessageDigest;
    
    import org.apache.commons.codec.binary.Hex;
    
    public class MD5Util {
    
        /**
         * 获取一个文件的md5值(可处理大文件)
         * @return md5 value
         */
        public static String getMD5(File file) {
            FileInputStream fileInputStream = null;
            try {
                MessageDigest MD5 = MessageDigest.getInstance("MD5");
                fileInputStream = new FileInputStream(file);
                byte[] buffer = new byte[8192];
                int length;
                while ((length = fileInputStream.read(buffer)) != -1) {
                    MD5.update(buffer, 0, length);
                }
                return new String(Hex.encodeHex(MD5.digest()));
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            } finally {
                try {
                    if (fileInputStream != null){
                        fileInputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }

    客户端:

    package com.sunyard.vst.test.client;
    
    import java.io.Closeable;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.RandomAccessFile;
    import java.net.Socket;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.sunyard.vst.utils.MD5Util;
    
    public class UploadClient extends Thread{
        
        private static Logger LOG = LoggerFactory.getLogger(UploadClient.class);
    
        
        DataInputStream in = null;
        DataOutputStream out = null;
        
        File file ;
        String ip;
        int port;
        
        public UploadClient(String filepath, String ip, int port) {
            this.file = new File(filepath);
            this.ip = ip;
            this.port = port ;
        }
        
        public UploadClient(File file, String ip, int port) {
            this.file = file;
            this.ip = ip;
            this.port = port ;
        }
    
        public static UploadClient upload(String filepath, String ip, int port) throws FileNotFoundException {
            File file = new File(filepath);
            if(!file.exists()) {
                throw new FileNotFoundException("上传文件不存在");
            }
            UploadClient instance = new UploadClient(file,ip,port) ;
            instance.start();
            return instance ;
        }
        
        public void run() {
            execute();
        }
        
        /**
         * 执行文件上传
         * @param filepath
         * @param ip 
         * @param port
         */
        public void execute() {
            String filename = file.getName();
            String filemd5 = MD5Util.getMD5(file);
            long filelength = file.length();
            String msg = filemd5 + "|" + filelength + "|" + filename;
            Socket socket = null;
            OutputStream output = null;
            InputStream input = null ;
            RandomAccessFile raf = null;
            try {
                socket = new Socket(ip,port);
                socket.setSoTimeout(30000);//设置超时时间:30秒
                output = socket.getOutputStream();
                input = socket.getInputStream();
                out = new DataOutputStream(output);
                in = new DataInputStream(input);
                
                //发送文本 (第一次交互)
                byte[] buffer = msg.getBytes("gbk");
                int len = buffer.length ;
                out.writeInt(len);
                out.write(buffer);
                out.flush();
                //收到响应
                len = in.readInt();
                buffer = new byte[len];
                in.read(buffer);
                msg = new String(buffer,"gbk");
                LOG.info("response : "+msg);
                /* 
                 * 收到报文后第二阶段
                 *   00 文件大小为0
                 *   01 文件存在,不需要重新上传
                 *   02 临时文件存在,竖线后是文件大小
                 *   03 文件存在,但是md5不同
                 */
                if(msg.startsWith("02")) {
                    
                    LOG.info("开始上传");
                    String[] strs = msg.split("\|");
                    int size = Integer.valueOf(strs[1]);
                    long offset = Long.valueOf(strs[2]);
                    raf = new RandomAccessFile(file,"r");
                    raf.seek(offset);
                    int length = 0;
                    byte[] buf = new byte[size];
                    while((length=raf.read(buf))>0){
                        out.write(buf,0,length);                            
                        out.flush();
                    }
                    
                    LOG.info("上传结束");
                    len = in.readInt();
                    buffer = new byte[len];
                    in.read(buffer);
                    msg = new String(buffer,"gbk");
                    LOG.info("上传后响应标示:"+msg);
                }else {
                    //TODO ...
                    LOG.info("未处理标示:"+msg);
                }
                
            } catch (Exception e) {
                LOG.error(e.getMessage(),e);
            } finally {
                close(raf);
                close(out);
                close(in);
                close(input);
                close(output);
            }
            
            
        }
        
        private void close(Closeable stream) {
            if(null != stream) {
                try {
                    stream.close();
                } catch (Exception e) {
                    LOG.error("关闭{}发生异常",stream.getClass().getSimpleName(),e);
                }
            }
        }
     
    }

    测试类:

    package com.sunyard.vst.test.client;
    
    import org.junit.Test;
    
    import com.sunyard.vst.upload.UploadServer;
    
    /**
     * @title 文件上传单元测试
     * @author gdl
     * @date 2020-12-9 10:35:07
     */
    public class UploadTest {
    
        /**
         * 启动服务端
         * @param args
         */
        public static void main(String[] args) {
            System.out.println("开始测试(服务端)");
            new Thread(new UploadServer(8889,20)).start();
            System.out.println("end/:-)");
        }
        
        /**
         * 启动客户端(单元测试启动)
         */
        @Test
        public void startClient() {
            System.out.println("开始测试(客户端)");
            new UploadClient("E:/1.jpg","127.0.0.1",8889).execute();
            System.out.println("end/:-)");
        }
    }
  • 相关阅读:
    mysql分表分库 ,读写分离
    二级域名解析设置及Apache 子域名配置
    PHP错误检测
    PHP缓存技术相关
    高并发一些处理办法
    memcached 安装与简单实用使用
    数据库入门
    函数
    结构体 枚举
    c# 6大类集合
  • 原文地址:https://www.cnblogs.com/hi-gdl/p/14110281.html
Copyright © 2011-2022 走看看