zoukankan      html  css  js  c++  java
  • java文件分片上传,断点续传

    百度的webUploader的前端开源插件实现的大文件分片上传功能

    前端部分

    前端页面代码如下,只需要修改自己的文件上传地址接口地址:

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>
             Web Uploader
        </title>
            <link rel="shortcut icon" href="http://fex.baidu.com/webuploader/images/favicon.ico">
            <link rel="stylesheet" type="text/css" href="js/bootstrap.min.css">
            <link rel="stylesheet" type="text/css" href="js/bootstrap-theme.min.css">
            <link rel="stylesheet" type="text/css" href="js/font-awesome.min.css">
            <link rel="stylesheet" type="text/css" href="js/syntax.css">
            <link rel="stylesheet" type="text/css" href="js/style.css">
            <link rel="stylesheet" type="text/css" href="js/webuploader.css">
            <link rel="stylesheet" type="text/css" href="js/demo.css">
    </head>
    <body>
    <div id="wrapper">
        <div class="page-body">
            <div id="post-container" class="container">
                <div class="page-container">
                    <h1 id="demo">Demo</h1>
                        <p>您可以尝试文件拖拽,使用QQ截屏工具,然后激活窗口后粘贴,或者点击添加图片按钮</p>
                    <div id="uploader" class="wu-example">
                        <div class="queueList">
                            <div id="dndArea" class="placeholder">
                                <div id="filePicker" class="webuploader-container">
                                    <div class="webuploader-pick">点击选择图片</div>
                                    <div id="rt_rt_1ctrotb75hv81prnco2vi318qc1" style="position: absolute; top: 0px; left: 448px; 168px; height: 44px; overflow: hidden; bottom: auto; right: auto;"><input type="file" name="file" class="webuploader-element-invisible" multiple="multiple" accept="image/*">
                                        <label style="opacity: 0; 100%; height: 100%; display: block; cursor: pointer; background: #ffffff;"></label>
                                    </div>
                                </div>
                                <p>或将照片拖到这里,单次最多可选300张</p>
                            </div>
                            <ul class="filelist"></ul>
                        </div>
                        <div class="statusBar" style="display:none;">
                            <div class="progress" style="display: none;">
                                <span class="text">0%</span>
                                <span class="percentage" style=" 0%;"></span>
                            </div><div class="info">共0张(0B),已上传0张</div>
                            <div class="btns">
                                <div id="filePicker2" class="webuploader-container"><div class="webuploader-pick">继续添加</div><div id="rt_rt_1ctrotb7j4opkv31e231b79k0a6" style="position: absolute; top: 0px; left: 0px; 1px; height: 1px; overflow: hidden;"><input type="file" name="file" class="webuploader-element-invisible" multiple="multiple" accept="image/*"><label style="opacity: 0; 100%; height: 100%; display: block; cursor: pointer; background: #ffffff;"></label></div></div><div class="uploadBtn state-pedding">开始上传</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div> 
    </div>
        <script type="text/javascript">
            var BASE_URL = '/webuploader';
        </script>
        <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
        <script type="text/javascript" src="js/bootstrap.min.js"></script>
        <script type="text/javascript" src="js/global.js"></script>   
        <script type="text/javascript" src="js/webuploader.js"></script>    
        <script type="text/javascript" src="js/demo.js"></script>
    </body>
    </html>

     前端js代码 demo.js,需要修改插件初始化的参数,和文件上传成功后的合并通知的地址。

    jQuery(function() {
        var $ = jQuery,    // just in case. Make sure it's not an other libaray.
            $wrap = $('#uploader'),
            // 图片容器
            $queue = $('<ul class="filelist"></ul>')
                .appendTo( $wrap.find('.queueList') ),
            // 状态栏,包括进度和控制按钮
            $statusBar = $wrap.find('.statusBar'),
            // 文件总体选择信息。
            $info = $statusBar.find('.info'),
            // 上传按钮
            $upload = $wrap.find('.uploadBtn'),
            // 没选择文件之前的内容。
            $placeHolder = $wrap.find('.placeholder'),
            // 总体进度条
            $progress = $statusBar.find('.progress').hide(),
            // 添加的文件数量
            fileCount = 0,
            // 添加的文件总大小
            fileSize = 0,
            // 优化retina, 在retina下这个值是2
            ratio = window.devicePixelRatio || 1,
            // 缩略图大小
            thumbnailWidth = 110 * ratio,
            thumbnailHeight = 110 * ratio,
            // 可能有pedding, ready, uploading, confirm, done.
            state = 'pedding',
            // 所有文件的进度信息,key为file id
            percentages = {},
            supportTransition = (function(){
                var s = document.createElement('p').style,
                    r = 'transition' in s ||
                          'WebkitTransition' in s ||
                          'MozTransition' in s ||
                          'msTransition' in s ||
                          'OTransition' in s;
                s = null;
                return r;
            })(),
            uploader;
        if ( !WebUploader.Uploader.support() ) {
            alert( 'Web Uploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器');
            throw new Error( 'WebUploader does not support the browser you are using.' );
        }
        // 实例化
        uploader = WebUploader.create({
            pick: {
                id: '#filePicker',
                label: '点击选择图片'
            },
            dnd: '#uploader .queueList',
            paste: document.body,
           /* accept: {
                title: 'Images',
                extensions: 'gif,jpg,jpeg,bmp,png',
                mimeTypes: 'image/*'
            },*/
            // swf文件路径
            swf: BASE_URL + '/js/Uploader.swf',
            disableGlobalDnd: true,
            //分片
            chunked: true,
            //每片大小 2M
            chunkSize: 20097152,
            //单片失败后重试次数
            chunkRetry: 10,
            //上传并发线程数
            thread: 1,
            // server: 'http://webuploader.duapp.com/server/fileupload.php',
            server: 'http://localhost:8080/uploadFile',
            fileNumLimit: 300,
            fileSizeLimit: 2000* 1024 * 1024,    // 2000 M
            fileSingleSizeLimit: 1000 * 1024 * 1024    // 1000 M
        });
        // 添加“添加文件”的按钮,
        uploader.addButton({
            id: '#filePicker2',
            label: '继续添加'
        });
        // 当有文件添加进来时执行,负责view的创建
        function addFile( file ) {
            var $li = $( '<li id="' + file.id + '">' +
                    '<p class="title">' + file.name + '</p>' +
                    '<p class="imgWrap"></p>'+
                    '<p class="progress"><span></span></p>' +
                    '</li>' ),
                $btns = $('<div class="file-panel">' +
                    '<span class="cancel">删除</span>' +
                    '<span class="rotateRight">向右旋转</span>' +
                    '<span class="rotateLeft">向左旋转</span></div>').appendTo( $li ),
                $prgress = $li.find('p.progress span'),
                $wrap = $li.find( 'p.imgWrap' ),
                $info = $('<p class="error"></p>'),
                showError = function( code ) {
                    switch( code ) {
                        case 'exceed_size':
                            text = '文件大小超出';
                            break;
                        case 'interrupt':
                            text = '上传暂停';
                            break;
                        default:
                            text = '上传失败,请重试';
                            break;
                    }
                    $info.text( text ).appendTo( $li );
                };
            if ( file.getStatus() === 'invalid' ) {
                showError( file.statusText );
            } else {
                // @todo lazyload
                $wrap.text( '预览中' );
                uploader.makeThumb( file, function( error, src ) {
                    if ( error ) {
                        $wrap.text( '不能预览' );
                        return;
                    }
                    var img = $('<img src="'+src+'">');
                    $wrap.empty().append( img );
                }, thumbnailWidth, thumbnailHeight );
                percentages[ file.id ] = [ file.size, 0 ];
                file.rotation = 0;
            }
            file.on('statuschange', function( cur, prev ) {
                if ( prev === 'progress' ) {
                    $prgress.hide().width(0);
                } else if ( prev === 'queued' ) {
                    $li.off( 'mouseenter mouseleave' );
                    $btns.remove();
                }
                // 成功
                if ( cur === 'error' || cur === 'invalid' ) {
                    console.log( file.statusText );
                    showError( file.statusText );
                    percentages[ file.id ][ 1 ] = 1;
                } else if ( cur === 'interrupt' ) {
                    showError( 'interrupt' );
                } else if ( cur === 'queued' ) {
                    percentages[ file.id ][ 1 ] = 0;
                } else if ( cur === 'progress' ) {
                    $info.remove();
                    $prgress.css('display', 'block');
                } else if ( cur === 'complete' ) {
                    $li.append( '<span class="success"></span>' );
                    alert("此文件已成功");
                    console.log("========="+file.size);
                    if(file.size>20097152){
                        $.post("http://localhost:8080/mergingChunks",{"id":file.id,"name":file.name,"size":file.size,"lastModifiedDate":file.lastModifiedDate},
                            function(data,status){
                              alert("Data: " + data + " Status: " + status);
                 });
                 }
                }
                $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );
            });
            $li.on( 'mouseenter', function() {
                $btns.stop().animate({height: 30});
            });
            $li.on( 'mouseleave', function() {
                $btns.stop().animate({height: 0});
            });
            $btns.on( 'click', 'span', function() {
                var index = $(this).index(),
                    deg;
                switch ( index ) {
                    case 0:
                        uploader.removeFile( file );
                        return;
                    case 1:
                        file.rotation += 90;
                        break;
                    case 2:
                        file.rotation -= 90;
                        break;
                }
                if ( supportTransition ) {
                    deg = 'rotate(' + file.rotation + 'deg)';
                    $wrap.css({
                        '-webkit-transform': deg,
                        '-mos-transform': deg,
                        '-o-transform': deg,
                        'transform': deg
                    });
                } else {
                    $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')');
                }
            });
            $li.appendTo( $queue );
        }
        // 负责view的销毁
        function removeFile( file ) {
            var $li = $('#'+file.id);
            delete percentages[ file.id ];
            updateTotalProgress();
            $li.off().find('.file-panel').off().end().remove();
        }
        function updateTotalProgress() {
            var loaded = 0,
                total = 0,
                spans = $progress.children(),
                percent;
            $.each( percentages, function( k, v ) {
                total += v[ 0 ];
                loaded += v[ 0 ] * v[ 1 ];
            } );
            percent = total ? loaded / total : 0;
            spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' );
            spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' );
            updateStatus();
        }
        function updateStatus() {
            var text = '', stats;
            if ( state === 'ready' ) {
                text = '选中' + fileCount + '张图片,共' +
                        WebUploader.formatSize( fileSize ) + '。';
            } else if ( state === 'confirm' ) {
                stats = uploader.getStats();
                if ( stats.uploadFailNum ) {
                    text = '已成功上传' + stats.successNum+ '张照片至XX相册,'+
                        stats.uploadFailNum + '张照片上传失败,<a class="retry" href="#">重新上传</a>失败图片或<a class="ignore" href="#">忽略</a>'
                }
            } else {
                stats = uploader.getStats();
                text = '共' + fileCount + '张(' +
                        WebUploader.formatSize( fileSize )  +
                        '),已上传' + stats.successNum + '张';
                if ( stats.uploadFailNum ) {
                    text += ',失败' + stats.uploadFailNum + '张';
                }
            }
            $info.html( text );
        }
        function setState( val ) {
            var file, stats;
            if ( val === state ) {
                return;
            }
            $upload.removeClass( 'state-' + state );
            $upload.addClass( 'state-' + val );
            state = val;
            switch ( state ) {
                case 'pedding':
                    $placeHolder.removeClass( 'element-invisible' );
                    $queue.parent().removeClass('filled');
                    $queue.hide();
                    $statusBar.addClass( 'element-invisible' );
                    uploader.refresh();
                    break;
                case 'ready':
                    $placeHolder.addClass( 'element-invisible' );
                    $( '#filePicker2' ).removeClass( 'element-invisible');
                    $queue.parent().addClass('filled');
                    $queue.show();
                    $statusBar.removeClass('element-invisible');
                    uploader.refresh();
                    break;
                case 'uploading':
                    $( '#filePicker2' ).addClass( 'element-invisible' );
                    $progress.show();
                    $upload.text( '暂停上传' );
                    break;
                case 'paused':
                    $progress.show();
                    $upload.text( '继续上传' );
                    break;
                case 'confirm':
                    $progress.hide();
                    $upload.text( '开始上传' ).addClass( 'disabled' );
                    stats = uploader.getStats();
                    if ( stats.successNum && !stats.uploadFailNum ) {
                        setState( 'finish' );
                        return;
                    }
                    break;
                case 'finish':
                    stats = uploader.getStats();
                    if ( stats.successNum ) {
                        alert( '文件上传已完成!' );//文件上传后服务器合并文件                  
                    } else {
                        // 没有成功的图片,重设
                        state = 'done';
                        location.reload();
                    }
                    break;
            }
            updateStatus();
        }
        uploader.onUploadProgress = function( file, percentage ) {
            var $li = $('#'+file.id),
                $percent = $li.find('.progress span');
            $percent.css( 'width', percentage * 100 + '%' );
            percentages[ file.id ][ 1 ] = percentage;
            updateTotalProgress();
        };
        uploader.onFileQueued = function( file ) {
            fileCount++;
            fileSize += file.size;
            if ( fileCount === 1 ) {
                $placeHolder.addClass( 'element-invisible' );
                $statusBar.show();
            }
            addFile( file );
            setState( 'ready' );
            updateTotalProgress();
        };
        uploader.onFileDequeued = function( file ) {
            fileCount--;
            fileSize -= file.size;
            if ( !fileCount ) {
                setState( 'pedding' );
            }
            removeFile( file );
            updateTotalProgress();
        };
        uploader.on( 'all', function( type ) {
            var stats;
            switch( type ) {
                case 'uploadFinished':
                    setState( 'confirm' );
                    break;
                case 'startUpload':
                    setState( 'uploading' );
                    break;
                case 'stopUpload':
                    setState( 'paused' );
                    break;
            }
        });
        uploader.onError = function( code ) {
            alert( 'Eroor: ' + code );
        };
        $upload.on('click', function() {
            if ( $(this).hasClass( 'disabled' ) ) {
                return false;
            }
            if ( state === 'ready' ) {
                uploader.upload();
            } else if ( state === 'paused' ) {
                uploader.upload();
            } else if ( state === 'uploading' ) {
                uploader.stop();
            }
        });
        $info.on( 'click', '.retry', function() {
            uploader.retry();
        } );
        $info.on( 'click', '.ignore', function() {
            alert( 'todo' );
        } );
        $upload.addClass( 'state-' + state );
        updateTotalProgress();
    });

     后台Controller层代码:

    package com.webFileUploader.Controller;
    import java.util.Map;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    import com.webFileUploader.exception.ErrorPremetersException;
    import com.webFileUploader.service.FileManagerService;
    import com.webFileUploader.utils.MutilFileUploadUtils;
    import com.webFileUploader.vo.MutilFileInfo;
    /**
     * @version 1.0
     * @author liangxh
     * @since 2018-12-04
     * @return 文件上传
     */
    @Controller
    public class uploadController {
        @Autowired
        private FileManagerService fileManagerService;    
        /**
          *     文件上传
          * @param fileInfo:文件参数实体类
          * @param file 附件字节码文件
          * @return 返回处理结果,请求头200:成功,500:失败
          * @throws Exception 
          */
        @RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
        @ResponseBody
        public Map<String, String> MutiluploadFile(MutilFileInfo fileinfo,@RequestParam(required=false,value="file")MultipartFile file,HttpServletResponse response) throws Exception {
            try {
                if(file != null && !file.isEmpty()){
                    if(MutilFileUploadUtils.checkMutiFilePremeter(fileinfo)){    //切片上传
                        fileManagerService.saveMutiBurstFiletoDir(fileinfo,file);
                    }else if(MutilFileUploadUtils.checkSingleFilePremeter(fileinfo)){//单文件整体上传
                        fileManagerService.saveSingleFiletoDir(fileinfo,file); 
                    }else {
                        throw new ErrorPremetersException("文件上传参数不合法");
                    }
                }else {
                    throw new ErrorPremetersException("文件上传附件字节流内容为空");
                }    
            } catch (Exception e) {
                e.printStackTrace();
                response.setStatus(500);
            }
            return null;
        }
        /**
          *     文件分片合并
          * @return 返回处理结果,请求头200:成功,500:失败
          * @throws Exception 
          */
        @RequestMapping(value = "/mergingChunks", method = RequestMethod.POST)
        @ResponseBody
       public Map<String, String> MutilMergingChunksForFile(MutilFileInfo fileinfo,HttpServletResponse response) throws Exception {
            try {            
                fileManagerService.MutilMergingChunks(fileinfo);
            } catch (Exception e) {
                e.printStackTrace();
                response.setStatus(500);
            }
            return null;
        }
        
        /**
          *     文件分片合并
          * @return 返回处理结果,请求头200:成功,500:失败
          * @throws Exception 
          */
       @RequestMapping(value = "/")
      public String uploaderView(HttpServletRequest request) throws Exception {
           HttpSession session = request.getSession();
           System.out.println(session);
            return "redirect:/index.html";
        }
       
    }

     后台Service实现层;先在服务端硬盘上创建一个要上传的文件相同的大文件,利用RandomAccessFile实现文件的随机读写。从而实现在分片上传时直接覆盖空白文件中的一小段数据,从而避免分片保存为临时文件再合并的IO消耗,

    package com.webFileUploader.service.impl;
    import java.io.File;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.locks.ReentrantLock;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    import com.webFileUploader.service.FileManagerService;
    import com.webFileUploader.utils.MutilFileUploadUtils;
    import com.webFileUploader.utils.SHA256Util;
    import com.webFileUploader.utils.SessionUtils;
    import com.webFileUploader.vo.MutilFileInfo;
    @Service
    public class FileManagerServiceImpl implements FileManagerService {
        
        @Value("${filePath.tempWorkBasePath}")
        private String tempWorkPath;//分片临时文件存放目录
        @Value("${filePath.saveFileBasePath}")
        private String saveFilePath;//文件存放目录
        private ReentrantLock filetempLock = new ReentrantLock();
        
        @Override
        public void saveMutiBurstFiletoDir(MutilFileInfo fileinfo, MultipartFile file) throws Exception {
            this.checkBaseDir(tempWorkPath);
            File tempFile = this.GenerateDirPathForCurrFile(fileinfo,"chunks");
            MutilFileUploadUtils.spaceFileWriter(file,tempFile,fileinfo);
        }
        @Override
        public void saveSingleFiletoDir(MutilFileInfo fileinfo, MultipartFile file) throws Exception {
            this.checkBaseDir(saveFilePath);
            File targetFile = this.GenerateDirPathForCurrFile(fileinfo,"single");
            MutilFileUploadUtils.saveFile2DirPath(file,targetFile);    
        }
        
        @Override
        synchronized public File  GenerateDirPathForCurrFile(MutilFileInfo fileinfo,String flag) throws Exception {
            String fileName = fileinfo.getName();
            String lastModifiedDate = fileinfo.getLastModifiedDate();
            long fileSize = fileinfo.getSize();
            String type = fileinfo.getType();
            String id = fileinfo.getId();
            String extName = fileName.substring(fileName.lastIndexOf("."));
            long timeStemp=System.currentTimeMillis();
            if("single".equals(flag)) {
                String fileNameSource = fileName+lastModifiedDate+fileSize+type+id+timeStemp;
                String fileDirName = SHA256Util.getSHA256StrJava(fileNameSource)+extName;
                File targetFile = new File(saveFilePath,fileDirName);
                while(targetFile.exists()){
                    fileNameSource=fileNameSource+"1";
                    fileDirName = SHA256Util.getSHA256StrJava(fileNameSource)+extName;
                    targetFile = new File(fileDirName);
                }
                return targetFile;
            }else if("chunks".equals(flag)) {
                String fileNameSource = fileSize+"_"+fileName+id+lastModifiedDate;
                String fileDirName = tempWorkPath+"/"+SHA256Util.getSHA256StrJava(fileNameSource)+extName+".temp";
                File tempFile = new File(fileDirName);//禁用FileInfo.exists()类, 防止缓存导致并发问题
                if(!(tempFile.exists()&&tempFile.isFile())){
                    filetempLock.lock();//上锁
                        if(!(tempFile.exists()&&tempFile.isFile())) {
                            MutilFileUploadUtils.readySpaceFile(fileinfo,tempFile);
                        }
                    filetempLock.unlock();//释放锁
                }
                tempFile = new File(fileDirName);
                return tempFile;
            }else{
                throw new Exception("目标文件生成失败");
            }
        }
        public void checkBaseDir(String baseDir) throws Exception {
            File file = new File(baseDir);
            if(!file.exists()&&!file.isDirectory()) {
                file.mkdirs();
            }    
        }
        @Override//文件合并
        public void MutilMergingChunks(MutilFileInfo fileinfo) throws Exception {
            // TODO Auto-generated method stub
            String fileName = fileinfo.getName();
            String lastModifiedDate = fileinfo.getLastModifiedDate();
            long fileSize = fileinfo.getSize();
            String id = fileinfo.getId();
            String extName = fileName.substring(fileName.lastIndexOf("."));
            String fileNameSource = fileSize+"_"+fileName+id+lastModifiedDate;
            String fileDirName = tempWorkPath+"/"+SHA256Util.getSHA256StrJava(fileNameSource)+extName+".temp";
            File tempFile = new File(fileDirName);
            if(tempFile.exists()&&tempFile.isFile()) {
                checkBaseDir(saveFilePath);
                String targetDirName = saveFilePath+"/"+SHA256Util.getSHA256StrJava(fileNameSource);
                File targetFile=new File(targetDirName+extName);
                while(targetFile.exists()&&targetFile.isFile()) {
                    targetDirName = targetDirName+"1";
                    targetFile=new File(targetDirName+extName);
                }
                System.out.println(targetFile.getAbsolutePath());
                if(tempFile.renameTo(targetFile)) {
                    System.out.println("文件重命名成功!");
                    //数据库操作
                    //数据库操作
                }else {
                    System.out.println("文件重命名失败!");
                    throw new Exception("临时文件重命名失败");
                }
                
            }
        }
    }

     实体类,为了便与操作,这里将所有的参数分装成了一个实体类;

    package com.webFileUploader.vo;
    import java.io.File;
    import java.util.Arrays;
    /**
     * @version 1.0
     * @author liangxh
     * @since 2018-12-04
     * @return 大文件上传实体类
     */
     public class MutilFileInfo {
        private String id; //文件id
        private String name; //文件名称
        private String type;//文件类型
        private String lastModifiedDate;//文件最后一次修改时间
        private Long size;//文件总大小
        //private Byte[] file;//副本字节流文件
        private Integer chunk;//当前分片序号
        private Integer chunks;//分片总数
        private File fileChunk;//文件临时分片
        private Boolean saved=false;//分片是否保存成功 默认值:false
        public Boolean getSaved() {
            return saved;
        }
        public void setSaved(Boolean saved) {
            this.saved = saved;
        }
        public String getId() {
            return id;
        }
        public File getFileChunk() {
            return fileChunk;
        }
        public void setFileChunk(File fileChunk) {
            this.fileChunk = fileChunk;
        }
        public void setId(String id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getType() {
            return type;
        }
        public void setType(String type) {
            this.type = type;
        }
        public String getLastModifiedDate() {
            return lastModifiedDate;
        }
        public void setLastModifiedDate(String lastModifiedDate) {
            this.lastModifiedDate = lastModifiedDate;
        }
        public Long getSize() {
            return size;
        }
        public void setSize(Long size) {
            this.size = size;
        }
        public Integer getChunk() {
            return chunk;
        }
        public void setChunk(Integer chunk) {
            this.chunk = chunk;
        }
        public Integer getChunks() {
            return chunks;
        }
        public void setChunks(Integer chunks) {
            this.chunks = chunks;
        }
        @Override
        public String toString() {
            return "MutilFileInfo [id=" + id + ", name=" + name + ", type=" + type + ", lastModifiedDate="
                    + lastModifiedDate + ", size=" + size + ", chunk=" + chunk + ", chunks=" + chunks + ", fileChunk="
                    + fileChunk + ", saved=" + saved + "]";
        }
        
        
    }

     文件上传工具类封装;

    package com.webFileUploader.utils;
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileFilter;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Map.Entry;
    import java.util.Set;
    import java.util.concurrent.ConcurrentHashMap;
    import org.springframework.web.multipart.MultipartFile;
    import com.webFileUploader.vo.MutilFileInfo;
    /**
     * @version 1.0
     * @author liangxh
     * @since 2018-12-04
     * @return 文件上传工具类
     */
    public class MutilFileUploadUtils {
        
        /**
          *      校验文件切片上传参数(字节流文件不能为空)
          * @param fileinfo:上传参数实体类
          * @return  判断是否文件切片上传,true:切片上传,false:单文件整体上传
          * @throws Exception 
          */
        public static Boolean checkMutiFilePremeter(MutilFileInfo fileinfo) {
            if(fileinfo!=null) {
                if(fileinfo.getChunks()!=null&&fileinfo.getChunk()!=null&&fileinfo.getChunks()>1&&fileinfo.getChunk()>=0&&fileinfo.getChunks()>fileinfo.getChunk()) {
                    return true;
                }else {
                    return false;
                }
            }else {
                return false;        
            }    
        }
        /**
          *      校验文件单文件上传参数(字节流文件不能为空)
          * @param fileinfo:上传参数实体类
          * @return  判断参数上传是否合法,true:符合单文件上传参数格式,false:不符合单文件格式
          * @throws Exception 
          */
        public static Boolean checkSingleFilePremeter(MutilFileInfo fileinfo) {
            if(fileinfo!=null) {
                if(fileinfo.getChunks()==null&&fileinfo.getChunk()==null) {
                    return true;
                }else {
                    return false;
                }
            }else {
                return false;        
            }    
        }    
        /**
          *      保存文件到指定目录
          * @param fileinfo:上传参数实体类
          * @throws Exception 
          */
        public static void saveFile2DirPath(MultipartFile file,File targetFile) throws Exception {
            if(targetFile.createNewFile()){
                file.transferTo(targetFile);
            }
        }
        /**
          *     创建空目标文件
         * @throws IOException 
          * @throws Exception 
          */
        public static void readySpaceFile(MutilFileInfo fileinfo,File tempFile) throws IOException{
            RandomAccessFile targetSpaceFile = new RandomAccessFile(tempFile, "rws");
            targetSpaceFile.setLength(fileinfo.getSize()); 
            System.out.println("创建文件:"+fileinfo.getSize());
            targetSpaceFile.close();
        }
        /**
          *     向空文件写入二进制数据
          * @param targetFile:目标文件
          * @param appenderFile:数据源文件
         * @throws Exception 
          */
        @SuppressWarnings("resource")
        public static void spaceFileWriter(MultipartFile file, File tempFile,MutilFileInfo fileInfo) throws Exception {
            long totalSpace = tempFile.getTotalSpace();
            RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
            BufferedInputStream sourceBuffer = new BufferedInputStream(file.getInputStream());
            Long startPointer = getFileWriterStartPointer(file, fileInfo);
            raf.seek(startPointer);//初始化文件指针起始位置
            byte[] bt = new byte[1024];
            int n=0;
            try {
                while ((n=sourceBuffer.read(bt))!=-1){
                    raf.write(bt);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(sourceBuffer!=null) {
                    sourceBuffer.close();
                }
                if(raf!=null) {
                    raf.close();
                }
            }    
        }
        /**
          *     计算指针开始位置
          * @param fileInfo:分片实体类
          * @param MultipartFile:文件流
          * @throws IOException 
          * @throws Exception 
          */
        synchronized public static Long getFileWriterStartPointer(MultipartFile file, MutilFileInfo fileInfo) throws Exception {
            // TODO Auto-generated method stub
            long chunkSize = file.getSize();
            Integer currChunk = fileInfo.getChunk();
            Integer allChunks = fileInfo.getChunks();
            Long allSize = fileInfo.getSize();
            if(currChunk<(allChunks-1)){
                long starter = chunkSize*currChunk;
                return starter;
            }else if(currChunk==(allChunks-1)){
                long starter = allSize-chunkSize;
                return starter;
            }else {
                throw new Exception("分片参数异常");
            }        
        }
    }

     DEMO下载地址:https://dwz.cn/fgXtRtnu

  • 相关阅读:
    电源锁
    Android的三种网络联接方式
    用tcpdump在手机上抓包
    图片出现波纹的问题
    Android 3.1以后 广播接收器的新机制
    OpenGL坐标
    用Messager进行IPC
    PHP 介绍
    View坐标,MotionEvent坐标, 二者的转换,可视区域
    OpenGL ES
  • 原文地址:https://www.cnblogs.com/xproer/p/10795061.html
Copyright © 2011-2022 走看看