百度的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 = $('
' + file.name + '
' + 94 ''+ 95 '' + 96 '
' +
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 MapMutiluploadFile(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 }