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">Demoh1>
                    <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; width: 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; width: 100%; height: 100%; display: block; cursor: pointer; background: rgb(255, 255, 255);">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="width: 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; width: 1px; height: 1px; overflow: hidden;"><input type="file" name="file" class="webuploader-element-invisible" multiple="multiple" accept="image/*"><label style="opacity: 0; width: 100%; height: 100%; display: block; cursor: pointer; background: rgb(255, 255, 255);">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,需要修改插件初始化的参数,和文件上传成功后的合并通知的地址。

  1 jQuery(function() {
  2     var $ = jQuery,    // just in case. Make sure it's not an other libaray.
  3         $wrap = $('#uploader'),
  4         // 图片容器
  5         $queue = $('
    ') 6 .appendTo( $wrap.find('.queueList') ), 7 // 状态栏,包括进度和控制按钮 8 $statusBar = $wrap.find('.statusBar'), 9 // 文件总体选择信息。 10 $info = $statusBar.find('.info'), 11 12 // 上传按钮 13 $upload = $wrap.find('.uploadBtn'), 14 15 // 没选择文件之前的内容。 16 $placeHolder = $wrap.find('.placeholder'), 17 18 // 总体进度条 19 $progress = $statusBar.find('.progress').hide(), 20 21 // 添加的文件数量 22 fileCount = 0, 23 24 // 添加的文件总大小 25 fileSize = 0, 26 27 // 优化retina, 在retina下这个值是2 28 ratio = window.devicePixelRatio || 1, 29 30 // 缩略图大小 31 thumbnailWidth = 110 * ratio, 32 thumbnailHeight = 110 * ratio, 33 34 // 可能有pedding, ready, uploading, confirm, done. 35 state = 'pedding', 36 37 // 所有文件的进度信息,key为file id 38 percentages = {}, 39 supportTransition = (function(){ 40 var s = document.createElement('p').style, 41 r = 'transition' in s || 42 'WebkitTransition' in s || 43 'MozTransition' in s || 44 'msTransition' in s || 45 'OTransition' in s; 46 s = null; 47 return r; 48 })(), 49 uploader; 50 if ( !WebUploader.Uploader.support() ) { 51 alert( 'Web Uploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器'); 52 throw new Error( 'WebUploader does not support the browser you are using.' ); 53 } 54 // 实例化 55 uploader = WebUploader.create({ 56 pick: { 57 id: '#filePicker', 58 label: '点击选择图片' 59 }, 60 dnd: '#uploader .queueList', 61 paste: document.body, 62 /* accept: { 63 title: 'Images', 64 extensions: 'gif,jpg,jpeg,bmp,png', 65 mimeTypes: 'image/*' 66 },*/ 67 // swf文件路径 68 swf: BASE_URL + '/js/Uploader.swf', 69 70 disableGlobalDnd: true, 71 //分片 72 chunked: true, 73 //每片大小 2M 74 chunkSize: 20097152, 75 //单片失败后重试次数 76 chunkRetry: 10, 77 //上传并发线程数 78 thread: 1, 79 // server: 'http://webuploader.duapp.com/server/fileupload.php', 80 server: 'http://localhost:8080/uploadFile', 81 fileNumLimit: 300, 82 fileSizeLimit: 2000* 1024 * 1024, // 2000 M 83 fileSingleSizeLimit: 1000 * 1024 * 1024 // 1000 M 84 }); 85 // 添加“添加文件”的按钮, 86 uploader.addButton({ 87 id: '#filePicker2', 88 label: '继续添加' 89 }); 90 // 当有文件添加进来时执行,负责view的创建 91 function addFile( file ) { 92 var $li = $( '
  • ' + 93 '

    ' + file.name + '

    ' + 94 '

    '+ 95 '

    ' + 96 '
  • ' ), 97 $btns = $('
    ' + 98 '删除' + 99 '向右旋转' + 100 '向左旋转
    ').appendTo( $li ), 101 $prgress = $li.find('p.progress span'), 102 $wrap = $li.find( 'p.imgWrap' ), 103 $info = $('

    '), 104 showError = function( code ) { 105 switch( code ) { 106 case 'exceed_size': 107 text = '文件大小超出'; 108 break; 109 case 'interrupt': 110 text = '上传暂停'; 111 break; 112 default: 113 text = '上传失败,请重试'; 114 break; 115 } 116 $info.text( text ).appendTo( $li ); 117 }; 118 if ( file.getStatus() === 'invalid' ) { 119 showError( file.statusText ); 120 } else { 121 // @todo lazyload 122 $wrap.text( '预览中' ); 123 uploader.makeThumb( file, function( error, src ) { 124 if ( error ) { 125 $wrap.text( '不能预览' ); 126 return; 127 } 128 var img = $(''); 129 $wrap.empty().append( img ); 130 }, thumbnailWidth, thumbnailHeight ); 131 132 percentages[ file.id ] = [ file.size, 0 ]; 133 file.rotation = 0; 134 } 135 file.on('statuschange', function( cur, prev ) { 136 if ( prev === 'progress' ) { 137 $prgress.hide().width(0); 138 } else if ( prev === 'queued' ) { 139 $li.off( 'mouseenter mouseleave' ); 140 $btns.remove(); 141 } 142 // 成功 143 if ( cur === 'error' || cur === 'invalid' ) { 144 console.log( file.statusText ); 145 showError( file.statusText ); 146 percentages[ file.id ][ 1 ] = 1; 147 } else if ( cur === 'interrupt' ) { 148 showError( 'interrupt' ); 149 } else if ( cur === 'queued' ) { 150 percentages[ file.id ][ 1 ] = 0; 151 } else if ( cur === 'progress' ) { 152 $info.remove(); 153 $prgress.css('display', 'block'); 154 } else if ( cur === 'complete' ) { 155 $li.append( '' ); 156 alert("此文件已成功"); 157 console.log("========="+file.size); 158 if(file.size>20097152){ 159 $.post("http://localhost:8080/mergingChunks",{"id":file.id,"name":file.name,"size":file.size,"lastModifiedDate":file.lastModifiedDate}, 160 function(data,status){ 161 alert("Data: " + data + "\nStatus: " + status); 162 }); 163 } 164 } 165 $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur ); 166 }); 167 $li.on( 'mouseenter', function() { 168 $btns.stop().animate({height: 30}); 169 }); 170 $li.on( 'mouseleave', function() { 171 $btns.stop().animate({height: 0}); 172 }); 173 $btns.on( 'click', 'span', function() { 174 var index = $(this).index(), 175 deg; 176 switch ( index ) { 177 case 0: 178 uploader.removeFile( file ); 179 return; 180 case 1: 181 file.rotation += 90; 182 break; 183 case 2: 184 file.rotation -= 90; 185 break; 186 } 187 if ( supportTransition ) { 188 deg = 'rotate(' + file.rotation + 'deg)'; 189 $wrap.css({ 190 '-webkit-transform': deg, 191 '-mos-transform': deg, 192 '-o-transform': deg, 193 'transform': deg 194 }); 195 } else { 196 $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')'); 197 } 198 }); 199 200 $li.appendTo( $queue ); 201 } 202 203 // 负责view的销毁 204 function removeFile( file ) { 205 var $li = $('#'+file.id); 206 delete percentages[ file.id ]; 207 updateTotalProgress(); 208 $li.off().find('.file-panel').off().end().remove(); 209 } 210 function updateTotalProgress() { 211 var loaded = 0, 212 total = 0, 213 spans = $progress.children(), 214 percent; 215 216 $.each( percentages, function( k, v ) { 217 total += v[ 0 ]; 218 loaded += v[ 0 ] * v[ 1 ]; 219 } ); 220 221 percent = total ? loaded / total : 0; 222 spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' ); 223 spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' ); 224 updateStatus(); 225 } 226 function updateStatus() { 227 var text = '', stats; 228 if ( state === 'ready' ) { 229 text = '选中' + fileCount + '张图片,共' + 230 WebUploader.formatSize( fileSize ) + '。'; 231 } else if ( state === 'confirm' ) { 232 stats = uploader.getStats(); 233 if ( stats.uploadFailNum ) { 234 text = '已成功上传' + stats.successNum+ '张照片至XX相册,'+ 235 stats.uploadFailNum + '张照片上传失败,重新上传失败图片或忽略' 236 } 237 } else { 238 stats = uploader.getStats(); 239 text = '共' + fileCount + '张(' + 240 WebUploader.formatSize( fileSize ) + 241 '),已上传' + stats.successNum + '张'; 242 if ( stats.uploadFailNum ) { 243 text += ',失败' + stats.uploadFailNum + '张'; 244 } 245 } 246 $info.html( text ); 247 } 248 function setState( val ) { 249 var file, stats; 250 if ( val === state ) { 251 return; 252 } 253 $upload.removeClass( 'state-' + state ); 254 $upload.addClass( 'state-' + val ); 255 state = val; 256 switch ( state ) { 257 case 'pedding': 258 $placeHolder.removeClass( 'element-invisible' ); 259 $queue.parent().removeClass('filled'); 260 $queue.hide(); 261 $statusBar.addClass( 'element-invisible' ); 262 uploader.refresh(); 263 break; 264 case 'ready': 265 $placeHolder.addClass( 'element-invisible' ); 266 $( '#filePicker2' ).removeClass( 'element-invisible'); 267 $queue.parent().addClass('filled'); 268 $queue.show(); 269 $statusBar.removeClass('element-invisible'); 270 uploader.refresh(); 271 break; 272 case 'uploading': 273 $( '#filePicker2' ).addClass( 'element-invisible' ); 274 $progress.show(); 275 $upload.text( '暂停上传' ); 276 break; 277 case 'paused': 278 $progress.show(); 279 $upload.text( '继续上传' ); 280 break; 281 case 'confirm': 282 $progress.hide(); 283 $upload.text( '开始上传' ).addClass( 'disabled' ); 284 stats = uploader.getStats(); 285 if ( stats.successNum && !stats.uploadFailNum ) { 286 setState( 'finish' ); 287 return; 288 } 289 break; 290 case 'finish': 291 stats = uploader.getStats(); 292 if ( stats.successNum ) { 293 alert( '文件上传已完成!' );//文件上传后服务器合并文件 294 } else { 295 // 没有成功的图片,重设 296 state = 'done'; 297 location.reload(); 298 } 299 break; 300 } 301 updateStatus(); 302 } 303 uploader.onUploadProgress = function( file, percentage ) { 304 var $li = $('#'+file.id), 305 $percent = $li.find('.progress span'); 306 $percent.css( 'width', percentage * 100 + '%' ); 307 percentages[ file.id ][ 1 ] = percentage; 308 updateTotalProgress(); 309 }; 310 uploader.onFileQueued = function( file ) { 311 fileCount++; 312 fileSize += file.size; 313 if ( fileCount === 1 ) { 314 $placeHolder.addClass( 'element-invisible' ); 315 $statusBar.show(); 316 } 317 addFile( file ); 318 setState( 'ready' ); 319 updateTotalProgress(); 320 }; 321 322 uploader.onFileDequeued = function( file ) { 323 fileCount--; 324 fileSize -= file.size; 325 if ( !fileCount ) { 326 setState( 'pedding' ); 327 } 328 removeFile( file ); 329 updateTotalProgress(); 330 }; 331 uploader.on( 'all', function( type ) { 332 var stats; 333 switch( type ) { 334 case 'uploadFinished': 335 setState( 'confirm' ); 336 break; 337 case 'startUpload': 338 setState( 'uploading' ); 339 break; 340 case 'stopUpload': 341 setState( 'paused' ); 342 break; 343 } 344 }); 345 uploader.onError = function( code ) { 346 alert( 'Eroor: ' + code ); 347 }; 348 349 $upload.on('click', function() { 350 if ( $(this).hasClass( 'disabled' ) ) { 351 return false; 352 } 353 if ( state === 'ready' ) { 354 uploader.upload(); 355 } else if ( state === 'paused' ) { 356 uploader.upload(); 357 } else if ( state === 'uploading' ) { 358 uploader.stop(); 359 } 360 }); 361 $info.on( 'click', '.retry', function() { 362 uploader.retry(); 363 } ); 364 $info.on( 'click', '.ignore', function() { 365 alert( 'todo' ); 366 } ); 367 $upload.addClass( 'state-' + state ); 368 updateTotalProgress(); 369 });

    后台Controller层代码:

     1 package com.webFileUploader.Controller;
     2 
     3 import java.util.Map;
     4 
     5 import javax.servlet.http.HttpServletRequest;
     6 import javax.servlet.http.HttpServletResponse;
     7 import javax.servlet.http.HttpSession;
     8 
     9 import org.springframework.beans.factory.annotation.Autowired;
    10 import org.springframework.stereotype.Controller;
    11 import org.springframework.web.bind.annotation.RequestMapping;
    12 import org.springframework.web.bind.annotation.RequestMethod;
    13 import org.springframework.web.bind.annotation.RequestParam;
    14 import org.springframework.web.bind.annotation.ResponseBody;
    15 import org.springframework.web.bind.annotation.RestController;
    16 import org.springframework.web.multipart.MultipartFile;
    17 
    18 import com.webFileUploader.exception.ErrorPremetersException;
    19 import com.webFileUploader.service.FileManagerService;
    20 import com.webFileUploader.utils.MutilFileUploadUtils;
    21 import com.webFileUploader.vo.MutilFileInfo;
    22 
    23 /**
    24  * @version 1.0
    25  * @author liangxh
    26  * @since 2018-12-04
    27  * @return 文件上传
    28  */
    29 @Controller
    30 public class uploadController {
    31     @Autowired
    32     private FileManagerService fileManagerService;    
    33     /**
    34       *     文件上传
    35       * @param fileInfo:文件参数实体类
    36       * @param file 附件字节码文件
    37       * @return 返回处理结果,请求头200:成功,500:失败
    38       * @throws Exception 
    39       */
    40     @RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
    41     @ResponseBody
    42     public Map MutiluploadFile(MutilFileInfo fileinfo,@RequestParam(required=false,value="file")MultipartFile file,HttpServletResponse response) throws Exception {
    43         try {
    44             if(file != null && !file.isEmpty()){
    45                 if(MutilFileUploadUtils.checkMutiFilePremeter(fileinfo)){    //切片上传
    46                     fileManagerService.saveMutiBurstFiletoDir(fileinfo,file);
    47                 }else if(MutilFileUploadUtils.checkSingleFilePremeter(fileinfo)){//单文件整体上传
    48                     fileManagerService.saveSingleFiletoDir(fileinfo,file); 
    49                 }else {
    50                     throw new ErrorPremetersException("文件上传参数不合法");
    51                 }
    52             }else {
    53                 throw new ErrorPremetersException("文件上传附件字节流内容为空");
    54             }    
    55         } catch (Exception e) {
    56             e.printStackTrace();
    57             response.setStatus(500);
    58         }
    59         return null;
    60     }
    61     /**
    62       *     文件分片合并
    63       * @return 返回处理结果,请求头200:成功,500:失败
    64       * @throws Exception 
    65       */
    66     @RequestMapping(value = "/mergingChunks", method = RequestMethod.POST)
    67     @ResponseBody
    68    public Map MutilMergingChunksForFile(MutilFileInfo fileinfo,HttpServletResponse response) throws Exception {
    69         try {            
    70             fileManagerService.MutilMergingChunks(fileinfo);
    71         } catch (Exception e) {
    72             e.printStackTrace();
    73             response.setStatus(500);
    74         }
    75         return null;
    76     }
    77     
    78     /**
    79       *     文件分片合并
    80       * @return 返回处理结果,请求头200:成功,500:失败
    81       * @throws Exception 
    82       */
    83    @RequestMapping(value = "/")
    84   public String uploaderView(HttpServletRequest request) throws Exception {
    85        HttpSession session = request.getSession();
    86        System.out.println(session);
    87         return "redirect:/index.html";
    88     }
    89    
    90 }

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

      1 package com.webFileUploader.service.impl;
      2 
      3 import java.io.File;
      4 import java.util.ArrayList;
      5 import java.util.Collections;
      6 import java.util.List;
      7 import java.util.concurrent.ConcurrentHashMap;
      8 import java.util.concurrent.locks.ReentrantLock;
      9 
     10 import org.springframework.beans.factory.annotation.Value;
     11 import org.springframework.stereotype.Service;
     12 import org.springframework.web.multipart.MultipartFile;
     13 
     14 import com.webFileUploader.service.FileManagerService;
     15 import com.webFileUploader.utils.MutilFileUploadUtils;
     16 import com.webFileUploader.utils.SHA256Util;
     17 import com.webFileUploader.utils.SessionUtils;
     18 import com.webFileUploader.vo.MutilFileInfo;
     19 
     20 @Service
     21 public class FileManagerServiceImpl implements FileManagerService {
     22     
     23     @Value("${filePath.tempWorkBasePath}")
     24     private String tempWorkPath;//分片临时文件存放目录
     25     @Value("${filePath.saveFileBasePath}")
     26     private String saveFilePath;//文件存放目录
     27     private ReentrantLock filetempLock = new ReentrantLock();
     28     
     29     @Override
     30     public void saveMutiBurstFiletoDir(MutilFileInfo fileinfo, MultipartFile file) throws Exception {
     31         this.checkBaseDir(tempWorkPath);
     32         File tempFile = this.GenerateDirPathForCurrFile(fileinfo,"chunks");
     33         MutilFileUploadUtils.spaceFileWriter(file,tempFile,fileinfo);
     34     }
     35     @Override
     36     public void saveSingleFiletoDir(MutilFileInfo fileinfo, MultipartFile file) throws Exception {
     37         this.checkBaseDir(saveFilePath);
     38         File targetFile = this.GenerateDirPathForCurrFile(fileinfo,"single");
     39         MutilFileUploadUtils.saveFile2DirPath(file,targetFile);    
     40     }
     41     
     42     @Override
     43     synchronized public File  GenerateDirPathForCurrFile(MutilFileInfo fileinfo,String flag) throws Exception {
     44         String fileName = fileinfo.getName();
     45         String lastModifiedDate = fileinfo.getLastModifiedDate();
     46         long fileSize = fileinfo.getSize();
     47         String type = fileinfo.getType();
     48         String id = fileinfo.getId();
     49         String extName = fileName.substring(fileName.lastIndexOf("."));
     50         long timeStemp=System.currentTimeMillis();
     51         if("single".equals(flag)) {
     52             String fileNameSource = fileName+lastModifiedDate+fileSize+type+id+timeStemp;
     53             String fileDirName = SHA256Util.getSHA256StrJava(fileNameSource)+extName;
     54             File targetFile = new File(saveFilePath,fileDirName);
     55             while(targetFile.exists()){
     56                 fileNameSource=fileNameSource+"1";
     57                 fileDirName = SHA256Util.getSHA256StrJava(fileNameSource)+extName;
     58                 targetFile = new File(fileDirName);
     59             }
     60             return targetFile;
     61         }else if("chunks".equals(flag)) {
     62             String fileNameSource = fileSize+"_"+fileName+id+lastModifiedDate;
     63             String fileDirName = tempWorkPath+"/"+SHA256Util.getSHA256StrJava(fileNameSource)+extName+".temp";
     64             File tempFile = new File(fileDirName);//禁用FileInfo.exists()类, 防止缓存导致并发问题
     65             if(!(tempFile.exists()&&tempFile.isFile())){
     66                 filetempLock.lock();//上锁
     67                     if(!(tempFile.exists()&&tempFile.isFile())) {
     68                         MutilFileUploadUtils.readySpaceFile(fileinfo,tempFile);
     69                     }
     70                 filetempLock.unlock();//释放锁
     71             }
     72             tempFile = new File(fileDirName);
     73             return tempFile;
     74         }else{
     75             throw new Exception("目标文件生成失败");
     76         }
     77     }
     78     public void checkBaseDir(String baseDir) throws Exception {
     79         File file = new File(baseDir);
     80         if(!file.exists()&&!file.isDirectory()) {
     81             file.mkdirs();
     82         }    
     83     }
     84     @Override//文件合并
     85     public void MutilMergingChunks(MutilFileInfo fileinfo) throws Exception {
     86         // TODO Auto-generated method stub
     87         String fileName = fileinfo.getName();
     88         String lastModifiedDate = fileinfo.getLastModifiedDate();
     89         long fileSize = fileinfo.getSize();
     90         String id = fileinfo.getId();
     91         String extName = fileName.substring(fileName.lastIndexOf("."));
     92         String fileNameSource = fileSize+"_"+fileName+id+lastModifiedDate;
     93         String fileDirName = tempWorkPath+"/"+SHA256Util.getSHA256StrJava(fileNameSource)+extName+".temp";
     94         File tempFile = new File(fileDirName);
     95         if(tempFile.exists()&&tempFile.isFile()) {
     96             checkBaseDir(saveFilePath);
     97             String targetDirName = saveFilePath+"/"+SHA256Util.getSHA256StrJava(fileNameSource);
     98             File targetFile=new File(targetDirName+extName);
     99             while(targetFile.exists()&&targetFile.isFile()) {
    100                 targetDirName = targetDirName+"1";
    101                 targetFile=new File(targetDirName+extName);
    102             }
    103             System.out.println(targetFile.getAbsolutePath());
    104             if(tempFile.renameTo(targetFile)) {
    105                 System.out.println("文件重命名成功!");
    106                 //数据库操作
    107                 //数据库操作
    108             }else {
    109                 System.out.println("文件重命名失败!");
    110                 throw new Exception("临时文件重命名失败");
    111             }
    112             
    113         }
    114     }
    115 }

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

     1 package com.webFileUploader.vo;
     2 
     3 import java.io.File;
     4 import java.util.Arrays;
     5 
     6 /**
     7  * @version 1.0
     8  * @author liangxh
     9  * @since 2018-12-04
    10  * @return 大文件上传实体类
    11  */
    12  public class MutilFileInfo {
    13     private String id; //文件id
    14     private String name; //文件名称
    15     private String type;//文件类型
    16     private String lastModifiedDate;//文件最后一次修改时间
    17     private Long size;//文件总大小
    18     //private Byte[] file;//副本字节流文件
    19     private Integer chunk;//当前分片序号
    20     private Integer chunks;//分片总数
    21     private File fileChunk;//文件临时分片
    22     private Boolean saved=false;//分片是否保存成功 默认值:false
    23     public Boolean getSaved() {
    24         return saved;
    25     }
    26     public void setSaved(Boolean saved) {
    27         this.saved = saved;
    28     }
    29     public String getId() {
    30         return id;
    31     }
    32     public File getFileChunk() {
    33         return fileChunk;
    34     }
    35     public void setFileChunk(File fileChunk) {
    36         this.fileChunk = fileChunk;
    37     }
    38     public void setId(String id) {
    39         this.id = id;
    40     }
    41     public String getName() {
    42         return name;
    43     }
    44     public void setName(String name) {
    45         this.name = name;
    46     }
    47     public String getType() {
    48         return type;
    49     }
    50     public void setType(String type) {
    51         this.type = type;
    52     }
    53     public String getLastModifiedDate() {
    54         return lastModifiedDate;
    55     }
    56     public void setLastModifiedDate(String lastModifiedDate) {
    57         this.lastModifiedDate = lastModifiedDate;
    58     }
    59     public Long getSize() {
    60         return size;
    61     }
    62     public void setSize(Long size) {
    63         this.size = size;
    64     }
    65     public Integer getChunk() {
    66         return chunk;
    67     }
    68     public void setChunk(Integer chunk) {
    69         this.chunk = chunk;
    70     }
    71     public Integer getChunks() {
    72         return chunks;
    73     }
    74     public void setChunks(Integer chunks) {
    75         this.chunks = chunks;
    76     }
    77     @Override
    78     public String toString() {
    79         return "MutilFileInfo [id=" + id + ", name=" + name + ", type=" + type + ", lastModifiedDate="
    80                 + lastModifiedDate + ", size=" + size + ", chunk=" + chunk + ", chunks=" + chunks + ", fileChunk="
    81                 + fileChunk + ", saved=" + saved + "]";
    82     }
    83     
    84     
    85 }

    文件上传工具类封装;

      1 package com.webFileUploader.utils;
      2 
      3 import java.io.BufferedInputStream;
      4 import java.io.BufferedOutputStream;
      5 import java.io.File;
      6 import java.io.FileFilter;
      7 import java.io.FileInputStream;
      8 import java.io.FileNotFoundException;
      9 import java.io.FileOutputStream;
     10 import java.io.IOException;
     11 import java.io.InputStream;
     12 import java.io.RandomAccessFile;
     13 import java.util.ArrayList;
     14 import java.util.Collections;
     15 import java.util.Comparator;
     16 import java.util.List;
     17 import java.util.Map.Entry;
     18 import java.util.Set;
     19 import java.util.concurrent.ConcurrentHashMap;
     20 
     21 import org.springframework.web.multipart.MultipartFile;
     22 
     23 import com.webFileUploader.vo.MutilFileInfo;
     24 /**
     25  * @version 1.0
     26  * @author liangxh
     27  * @since 2018-12-04
     28  * @return 文件上传工具类
     29  */
     30 public class MutilFileUploadUtils {
     31     
     32     /**
     33       *      校验文件切片上传参数(字节流文件不能为空)
     34       * @param fileinfo:上传参数实体类
     35       * @return  判断是否文件切片上传,true:切片上传,false:单文件整体上传
     36       * @throws Exception 
     37       */
     38     public static Boolean checkMutiFilePremeter(MutilFileInfo fileinfo) {
     39         if(fileinfo!=null) {
     40             if(fileinfo.getChunks()!=null&&fileinfo.getChunk()!=null&&fileinfo.getChunks()>1&&fileinfo.getChunk()>=0&&fileinfo.getChunks()>fileinfo.getChunk()) {
     41                 return true;
     42             }else {
     43                 return false;
     44             }
     45         }else {
     46             return false;        
     47         }    
     48     }
     49     /**
     50       *      校验文件单文件上传参数(字节流文件不能为空)
     51       * @param fileinfo:上传参数实体类
     52       * @return  判断参数上传是否合法,true:符合单文件上传参数格式,false:不符合单文件格式
     53       * @throws Exception 
     54       */
     55     public static Boolean checkSingleFilePremeter(MutilFileInfo fileinfo) {
     56         if(fileinfo!=null) {
     57             if(fileinfo.getChunks()==null&&fileinfo.getChunk()==null) {
     58                 return true;
     59             }else {
     60                 return false;
     61             }
     62         }else {
     63             return false;        
     64         }    
     65     }    
     66     /**
     67       *      保存文件到指定目录
     68       * @param fileinfo:上传参数实体类
     69       * @throws Exception 
     70       */
     71     public static void saveFile2DirPath(MultipartFile file,File targetFile) throws Exception {
     72         if(targetFile.createNewFile()){
     73             file.transferTo(targetFile);
     74         }
     75     }
     76     /**
     77       *     创建空目标文件
     78      * @throws IOException 
     79       * @throws Exception 
     80       */
     81     public static void readySpaceFile(MutilFileInfo fileinfo,File tempFile) throws IOException{
     82         RandomAccessFile targetSpaceFile = new RandomAccessFile(tempFile, "rws");
     83         targetSpaceFile.setLength(fileinfo.getSize()); 
     84         System.out.println("创建文件:"+fileinfo.getSize());
     85         targetSpaceFile.close();
     86     }
     87     /**
     88       *     向空文件写入二进制数据
     89       * @param targetFile:目标文件
     90       * @param appenderFile:数据源文件
     91      * @throws Exception 
     92       */
     93     @SuppressWarnings("resource")
     94     public static void spaceFileWriter(MultipartFile file, File tempFile,MutilFileInfo fileInfo) throws Exception {
     95         long totalSpace = tempFile.getTotalSpace();
     96         RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
     97         BufferedInputStream sourceBuffer = new BufferedInputStream(file.getInputStream());
     98         Long startPointer = getFileWriterStartPointer(file, fileInfo);
     99         raf.seek(startPointer);//初始化文件指针起始位置
    100         byte[] bt = new byte[1024];
    101         int n=0;
    102         try {
    103             while ((n=sourceBuffer.read(bt))!=-1){
    104                 raf.write(bt);
    105             }
    106         } catch (IOException e) {
    107             e.printStackTrace();
    108         }finally {
    109             if(sourceBuffer!=null) {
    110                 sourceBuffer.close();
    111             }
    112             if(raf!=null) {
    113                 raf.close();
    114             }
    115         }    
    116     }
    117     /**
    118       *     计算指针开始位置
    119       * @param fileInfo:分片实体类
    120       * @param MultipartFile:文件流
    121       * @throws IOException 
    122       * @throws Exception 
    123       */
    124     synchronized public static Long getFileWriterStartPointer(MultipartFile file, MutilFileInfo fileInfo) throws Exception {
    125         // TODO Auto-generated method stub
    126         long chunkSize = file.getSize();
    127         Integer currChunk = fileInfo.getChunk();
    128         Integer allChunks = fileInfo.getChunks();
    129         Long allSize = fileInfo.getSize();
    130         if(currChunk<(allChunks-1)){
    131             long starter = chunkSize*currChunk;
    132             return starter;
    133         }else if(currChunk==(allChunks-1)){
    134             long starter = allSize-chunkSize;
    135             return starter;
    136         }else {
    137             throw new Exception("分片参数异常");
    138         }        
    139     }
    140 }

     

    你可能感兴趣的:(java文件分片上传,断点续传)