分片断点上传及下载
分片上传
配置文件
1 2 3 4 5
| spring: servlet: multipart: enabled: false
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.5.5</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.14</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
|
前端文件
前端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>webuploader</title> </head>
<link rel="stylesheet" type="text/css" href="webuploader.css"> <script src="jquery-1.11.1.js"></script> <script src="webuploader.js"></script> <style> #upload-container, #upload-list{width: 500px; margin: 0 auto; } #upload-container{cursor: pointer; border-radius: 15px; background: #EEEFFF; height: 200px;} #upload-list{height: 800px; border: 1px solid #EEE; border-radius: 5px; margin-top: 10px; padding: 10px 20px;} #upload-container>span{widows: 100%; text-align: center; color: gray; display: block; padding-top: 15%;} .upload-item{margin-top: 5px; padding-bottom: 5px; border-bottom: 1px dashed gray;} .percentage{height: 5px; background: green;} .btn-delete, .btn-retry{cursor: pointer; color: gray;} .btn-delete:hover{color: orange;} .btn-retry:hover{color: green;} </style>
<body> <div id="upload-container"> <span>点击或将文件拖拽至此上传</span> </div> <div id="upload-list"> </div> <button id="picker" style="display: none;">点击上传文件</button> </body>
<script> $('#upload-container').click(function(event) { $("#picker").find('input').click(); }); var uploader = WebUploader.create({ auto: true, swf: 'Uploader.swf', server: 'http://localhost:8080/upload', dnd: '#upload-container', pick: '#picker', multiple: true, chunked: true, threads: 20, method: 'POST', fileSizeLimit: 1024*1024*1024*10, fileSingleSizeLimit: 1024*1024*1024*5, fileVal:'upload' });
uploader.on("beforeFileQueued", function(file) { console.log(file); });
uploader.on('fileQueued', function(file) { console.log(file.ext); console.log(file.size); console.log(file.name); var html = '<div class="upload-item"><span>文件名:'+file.name+'</span><span data-file_id="'+file.id+'" class="btn-delete">删除</span><span data-file_id="'+file.id+'" class="btn-retry">重试</span><div class="percentage '+file.id+'" style="width: 0%;"></div></div>'; $('#upload-list').append(html); uploader.md5File( file )
.progress(function(percentage) { console.log('Percentage:', percentage); })
.then(function(val) { console.log('md5 result:', val); }); });
uploader.on('uploadProgress', function(file, percentage) { console.log(percentage * 100 + '%'); var width = $('.upload-item').width(); $('.'+file.id).width(width*percentage); });
uploader.on('uploadSuccess', function(file, response) { console.log(file.id+"传输成功"); });
uploader.on('uploadError', function(file) { console.log(file); console.log(file.id+'upload error') });
$('#upload-list').on('click', '.upload-item .btn-delete', function() { file_id = $(this).data('file_id'); uploader.removeFile(file_id, true); console.log(uploader.getFiles()); });
$('#upload-list').on('click', '.btn-retry', function() { uploader.retry($(this).data('file_id')); });
uploader.on('uploadComplete', function(file) { console.log(uploader.getFiles()); }); </script> </html>
|
后端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
@Controller public class UploadAndDownloadController {
private final static String UTF8 ="utf-8";
private final static String UPLOAD_DOWNLOAD_PATH = "D:\\upload_test";
@ResponseBody @RequestMapping("/upload") public void upload(HttpServletRequest request, HttpServletResponse response) throws Exception { response.setCharacterEncoding(UTF8); Integer schunk = null; Integer schunks = null; String name = null; BufferedOutputStream os = null; try { DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setSizeThreshold(1024); factory.setRepository(new File(UPLOAD_DOWNLOAD_PATH));
ServletFileUpload uploaded = new ServletFileUpload(factory); uploaded.setFileSizeMax(5L * 1024L * 1024L * 1024L); uploaded.setSizeMax(10L * 1024L * 1024L * 1024L);
List<FileItem> items = uploaded.parseRequest(request); for (FileItem item : items) { if(item.isFormField()){ if("chunk".equals(item.getFieldName())){ schunk = Integer.parseInt(item.getString(UTF8)); } if("chunks".equals(item.getFieldName())){ schunks = Integer.parseInt(item.getString(UTF8)); } if("name".equals(item.getFieldName())){ name = item.getString(UTF8); } } } for(FileItem item : items){ if(!item.isFormField()){ String temFileName = name; if(name != null){ if(schunk != null){ temFileName = schunk +"_"+name; } File temFile = new File(UPLOAD_DOWNLOAD_PATH,temFileName); if(!temFile.exists()){ item.write(temFile); } } } } if(schunk != null && schunk.intValue() == schunks.intValue()-1){ File tempFile = new File(UPLOAD_DOWNLOAD_PATH,name); os = new BufferedOutputStream(new FileOutputStream(tempFile));
for(int i=0 ;i<schunks;i++){ File file = new File(UPLOAD_DOWNLOAD_PATH,i+"_"+name); while(!file.exists()){ Thread.sleep(100); file = new File(UPLOAD_DOWNLOAD_PATH,i+"_"+name); } byte[] bytes = FileUtils.readFileToByteArray(file); os.write(bytes); os.flush(); file.delete(); } os.flush(); } response.getWriter().write("上传成功"+name); }finally { try { if (os!=null){ os.close(); } } catch (Exception e) { e.printStackTrace(); } } } }
|
示例
分片下载
服务器端代码
如果只实现了服务器代码,未实现客户端代码 则与 普通下载无任何差异
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
@Controller public class UploadAndDownloadController {
private final static String UTF8 ="utf-8";
private final static String UPLOAD_DOWNLOAD_PATH = "D:\\upload_test";
@RequestMapping("/download") public void downLoadFile(HttpServletRequest request, HttpServletResponse response,String downloadFileName) throws Exception { if (downloadFileName==null|| "".equals(downloadFileName)) { return; } File file = new File(UPLOAD_DOWNLOAD_PATH+"\\"+downloadFileName); response.setCharacterEncoding(UTF8); InputStream is = null; OutputStream os = null; try{ long fSize = file.length(); response.setContentType("application/x-download"); String fileName = URLEncoder.encode(file.getName(),UTF8); response.addHeader("Content-Disposition","attachment;filename=" + fileName); response.setHeader("Accept-Range","bytes"); response.setHeader("fSize",String.valueOf(fSize)); response.setHeader("fName",fileName);
long pos = 0,last = fSize-1,sum = 0; if(null != request.getHeader("Range")){ response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
String numRange = request.getHeader("Range").replaceAll("bytes=",""); String[] strRange = numRange.split("-"); if(strRange.length == 2){ pos = Long.parseLong(strRange[0].trim()); last = Long.parseLong(strRange[1].trim()); if(last > fSize-1){ last = fSize-1; } }else{ pos = Long.parseLong(numRange.replaceAll("-","").trim()); } } long rangeLenght = last - pos +1; String contentRange = "bytes " + pos + "-" + last + "/" + fSize; response.setHeader("Content-Range",contentRange); response.setHeader("Content-Lenght",String.valueOf(rangeLenght));
os = new BufferedOutputStream(response.getOutputStream()); is = new BufferedInputStream(new FileInputStream(file)); is.skip(pos); byte[] buffer = new byte[1024]; int lenght = 0; while(sum < rangeLenght){ lenght = is.read(buffer,0,((rangeLenght-sum) <= buffer.length ? ((int)(rangeLenght-sum)) : buffer.length)); sum = sum+ lenght; os.write(buffer,0,lenght); } System.out.println("下载完成"); }finally { try { if(is != null){ is.close(); } if(os != null){ os.close(); } } catch (Exception e) { e.printStackTrace(); } } } }
|
客户端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
@RestController public class DownloadClient {
private final static long PER_PAGE = 1024L * 1024L * 50L;
private final static String DOWNPATH = "D:\\upload_test\\mytest";
ExecutorService pool = Executors.newFixedThreadPool(10);
@RequestMapping("/downloadFile") public String downloadFile(String downloadFileName) throws Exception { if(downloadFileName==null||"".equals(downloadFileName)){ return "error"; } FileInfo fileInfo = download( 0, 10, -1, downloadFileName,null); long pages = fileInfo.fSize / PER_PAGE; for(long i=0;i<=pages; i++){ pool.submit(new Download(i*PER_PAGE,(i+1)*PER_PAGE-1,i,downloadFileName,fileInfo.fName)); }
return "success"; }
@AllArgsConstructor @NoArgsConstructor private static class FileInfo{ long fSize; String fName; }
@AllArgsConstructor @NoArgsConstructor private class Download implements Runnable{ long start; long end; long page; String downloadFileName; String fName;
@Override public void run() { try { FileInfo info = download( start, end, page, downloadFileName,fName); } catch (Exception e) { e.printStackTrace(); } } }
private FileInfo download(long start,long end,long page,String downloadFileName,String fName) throws Exception { File file = new File(DOWNPATH,page+"-"+fName); if(page!=-1 && file.exists() && file.length()==PER_PAGE){ return null; }
HttpClient client = HttpClients.createDefault(); HttpGet httpGet = new HttpGet("http://127.0.0.1:8080/download?downloadFileName="+downloadFileName); httpGet.setHeader("Range","bytes="+start+"-"+end); HttpResponse response = client.execute(httpGet);
HttpEntity entity = response.getEntity(); InputStream is = entity.getContent();
String fSize = response.getFirstHeader("fSize").getValue(); fName = URLDecoder.decode(response.getFirstHeader("fName").getValue(),"utf-8");
FileOutputStream fis = new FileOutputStream(file); byte[] buffer = new byte[1024]; int ch =0; while((ch = is.read(buffer)) != -1){ fis.write(buffer,0,ch); }
try { is.close(); fis.flush(); fis.close(); } catch (IOException e) { e.printStackTrace(); }
if(end - Long.parseLong(fSize) >= 0){ mergeFile(fName,page); } return new FileInfo(Long.parseLong(fSize), fName); }
private void mergeFile(String fName, long page) throws Exception { File tempFile = new File(DOWNPATH,fName); BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile));
for(int i=0 ;i<=page;i++){ File file = new File(DOWNPATH,i+"-"+fName); while(!file.exists() || (i != page && file.length() < PER_PAGE)){ Thread.sleep(100); } byte[] bytes = FileUtils.readFileToByteArray(file); os.write(bytes); os.flush(); file.delete(); }
File file = new File(DOWNPATH,-1+"-null"); file.delete(); os.flush(); try { os.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
提示
如果只实现了服务器代码,未实现客户端代码 则与 普通下载无任何差异,在此就不进行演示
如果要实现客户端的分片下载,需同时实现上面两段代码,因为客户端代码需去请求服务器端代码,来实现分片下载,请注意客户端代码的第110行
在请求时使用客户端代码的请求方式就能实现客户端的分片下载,请看一下示例
示例