文件上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @PostMapping("/upload") public R<String> uploadFileAndPicture(MultipartFile e) { String originFileName = e.getOriginalFilename(); String suffix = originFileName.substring(originFileName.lastIndexOf(".")); File dir = new File(basepath + originFileName);
if(!dir.exists()){ dir.mkdirs(); } try{ e.transferTo(new File(basepath + originFileName));
}catch(Exception ex){ log.info("{}",ex.getMessage()); } log.info("{}", originFileName);
return R.success("发送成功"); }
|
文件下载 - 点击文件之后可下载
后端 - java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @GetMapping("/downloadfile") public String downloadFile(HttpServletResponse response, HttpServletRequest request, String fileName, String extend) throws IOException { JSONObject result = new JSONObject(); File file = new File(basepath + fileName); log.info("{}", extend); if (!file.exists()) { result.put("error", "下载的文件不存在"); return result.toString(); } response.reset(); response.setContentType(extend); response.setCharacterEncoding("utf-8"); response.setContentLength((int) file.length()); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8")); byte[] readeBytes = FileUtil.readBytes(file); OutputStream os = response.getOutputStream(); os.write(readeBytes); result.put("success", "下载成功"); return result.toString(); }
|
前端 - vue
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
| axios.get(baseUrl + "/api/downloadfile", { params: { fileName: item.fileName, extend: item.extend }, responseType: "arraybuffer" })
.then(res=>new Blob([res], {type: item.extend}))
.then(blob=> new File([blob], item.fileName)) .then(file=>{ item.msg = file let reader = new FileReader() let fileNameTemp = item.fileName reader.onloadend = (es)=>{ item.msg = es.target.result item.fileName = fileNameTemp console.log(item.msg)
if(item.chatType === 1){ srcImgList.value.push(item.msg) } } reader.readAsDataURL(file) })
|
断点续传
断点上传
后端
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
| @PostMapping("/uploadslice")
public R<Integer> uploadchunks(@RequestPart("file")MultipartFile file , @RequestPart("hash") String hash, @RequestPart("chunkcnt") String chunkcnt ,@RequestPart("filename") String filename, @RequestPart("totalCnt") String totalCnt) throws IOException, InterruptedException { log.info("{}", filename); String suffix = filename.substring(filename.lastIndexOf(".")); String prefix = filename.substring(0, filename.lastIndexOf(".") - 1); log.info("{}", suffix); log.info("{}", prefix); File dir = new File(basepath + hash+ "\\" + prefix+ "_" + chunkcnt); if(!dir.exists()){ dir.mkdirs(); } file.transferTo(dir); if(uploadcache.containsKey(hash)) uploadcache.put(hash, uploadcache.get(hash) + 1); else uploadcache.put(hash, 0); log.info("当前写了: {}", chunkcnt); if(chunkcnt.equals(totalCnt)){ boolean ismerge = false; while(!ismerge){ int n = Integer.parseInt(totalCnt); for(int i = 0; i <= n; ++i){ File fileitem = new File(basepath + hash + "\\" + prefix + "_" + i); if(!fileitem.exists()){ log.info("还有文件没有输入结束"); break; } if(i == n) ismerge = true; } } log.info("当前的文件为{}/{}",chunkcnt ,totalCnt); } return R.success(1); }
|
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
| @RequestMapping("/merge") public R<Integer> fileMerge(@RequestBody Map map) throws IOException { String hash = map.get("hash").toString(); String filename = map.get("filename").toString(); Integer totalCnt = Integer.valueOf(map.get("totalCnt").toString()); log.info("开始合并"); String suffix = filename.substring(filename.lastIndexOf(".")); String prefix = filename.substring(0, filename.lastIndexOf(".") - 1); File mergeFile = new File(basepath + filename); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(mergeFile)); log.info("{}", basepath + hash); for(int i = 0; i <= totalCnt; ++i){
File file = new File(basepath + hash + "\\" + prefix + "_" +Integer.toString(i)); log.info("{}:{}", file.length(), file.getName()); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); } bis.close(); } bos.flush(); bos.close(); log.info("合并成功"); return R.success(1); }
|
1 2 3 4 5 6 7
| @RequestMapping("/reupload") public R<Integer> reUpload(@RequestBody Map map) { String hash = map.get("result").toString(); if(!uploadcache.containsKey(hash)) return R.success(0); return R.success(uploadcache.get(hash) + 1); }
|
前端
你需要先下载 md5 的包才能继续往下看, 指令为npm install spark-md5
- 将文件的内容进行分片(我这里默认是 1024 KB)
1 2 3 4 5 6 7 8
| function createChunks(files, chunkSize) { const results = [] for(let i = 0; i < files.size; i += chunkSize){ results.push(files.slice(i, i + chunkSize)) } return results }
|
- 上面的createChunks函数返回了 数组 , 我们用一个变量接受它,
1 2
| let chunks = createChunks(file, 1024 * 1024) hash(chunks)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function hash(chunks){ const spark = new SparkMD5 return new Promise(resovle=>{ function _read(i){ if(i >= chunks.length){ resovle(spark.end()); return ; } const blob = chunks[i] const reader = new FileReader reader.onload = e=>{ const bytes = e.target.result spark.append(bytes) _read(i + 1) } reader.readAsArrayBuffer(blob) } _read(0) }) }
|
1
| let reuploadcnt = await reUpload(reuploadobj)
|
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
| for(let i = reuploadcnt; i < chunks.length; ++i){ if(stopupload.value[index] === true){ return } let chunkcnt = i let md5 = result const formData = new FormData() const blob = chunks[i] formData.append('file', blob) formData.append('hash', result) formData.append('chunkcnt', i) formData.append('filename', files.name) formData.append('totalCnt', chunks.length - 1) await axios.post('/api/uploadslice', (formData),{ onUploadProgress: function(progressEvent){ function sendHeartbeat() { setTimeout(function() { if (socket.readyState === WebSocket.OPEN) { socket.send(''); sendHeartbeat(); } else { console.log('WebSocket连接已关闭'); } }, 5000); } if( valueUploadList.value[index] + ( (progressEvent.loaded * 100) / filesizes) <= ((i + 1) * 1024 * 1024 * 100) / filesizes) valueUploadList.value[index] += ( (progressEvent.loaded * 100) / filesizes) else valueUploadList.value[index] = ((i + 1) * 1024 * 1024 * 100) / filesizes if(stopupload.value[index] === true){ return } } }) if(stopupload.value[index] === true){ return }
}
|
1 2 3 4 5 6
| let merge = { hash: result, filename: files.name, totalCnt: chunks.length - 1 } const finish = await fileMerge(merge)
|
断点下载
类似于断点上传, 只不过是把过程放过来
后端
1 2 3 4 5 6
| @RequestMapping("/getsize") public R<Long> getFileSize(@RequestBody Map map){ String fileName = map.get("fileName").toString(); File file = new File(basepath + fileName); return R.success(file.length()); }
|
1 2 3 4 5 6 7 8
| @PostMapping("/redownload") public R<Integer> redownload(@RequestBody Map map){ String fileName = map.get("fileName").toString(); String userid = map.get("userid").toString(); String encrypted= DigestUtils.md5DigestAsHex((basepath + fileName + userid).getBytes()); if(downloadcache.containsKey(encrypted) == false) return R.success(0); return R.success(downloadcache.get(encrypted) + 1); }
|
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
| @RequestMapping("/downloadslicefile") public String downloadSliceFile(HttpServletResponse response, HttpServletRequest request , String fileName, String extend, Long start, Long end, int curcnt, String userid, int totalcnt) throws IOException { int contentLength = request.getContentLength(); log.info("{} : {}" , start, end); JSONObject result = new JSONObject(); File file = new File(basepath + fileName); if (!file.exists()) { result.put("error", "下载的文件不存在"); return result.toString(); } response.reset(); response.setCharacterEncoding("utf-8"); response.setContentLength((int) file.length()); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8")); response.setHeader("Accept-Range", "bytes"); String contentRange = String.valueOf(new StringBuffer("bytes ").append(start).append("-").append(end)); response.setHeader("Content-Range", contentRange); response.setHeader("Content-Length", String.valueOf(end - start + 1)); RandomAccessFile rf = new RandomAccessFile(basepath + fileName, "r"); rf.seek(start); OutputStream os = response.getOutputStream(); byte[] bytes = new byte[(int) (end - start + 1)]; int len = rf.read(bytes); if(len != -1) os.write(bytes ,0, (int) (end - start + 1)); rf.close(); os.close(); result.put("success", "文件下载成功");
String encrypted= DigestUtils.md5DigestAsHex((basepath + fileName + userid).getBytes()); downloadcache.put(encrypted, curcnt); log.info("{}: {}", curcnt, totalcnt); if(curcnt == totalcnt){ downloadcache.remove(encrypted); } return result.toString();
}
|
前端
1
| let sizehigh = await getSize(getsizeobj)
|
1
| let downloadcnt = await redownload(redownloadobj)
|
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
| for(let i = startsize;i < sizehigh; i += len){ len = Math.min(sizehigh - i, 1024 * 1024) await axios.get('/api/downloadslicefile',{ params: { fileName: item.fileName, extend: item.extend, start: i, end: i + len - 1, curcnt: curcnt, userid: userstore.userid, totalcnt: totalcnt }, headers: { range: `bytes=${i}-${i + len - 1}` }, responseType: "arraybuffer", onDownloadProgress(progressEvent){ if(valueList.value[index] + (progressEvent.loaded * 100) / sizehigh > ((curcnt + 1) * 1024 * 1024) / sizehigh * 100) valueList.value[index] = ((curcnt + 1) * 1024 * 1024) / sizehigh * 100 else valueList.value[index] += (progressEvent.loaded * 100) / sizehigh; console.log('当前进度条位: ', valueList.value[index] + ' 正在传送 ' + curcnt + ' ' + ((curcnt + 1) * 1024 * 1024) / sizehigh * 100); if(stopdownload.get(index) === true){ arraymap.set(index, arrayBufferArray) console.log(curcnt); return } } }).then(clip=>{ arrayBufferArray.push(clip) curcnt += 1 }) }
|
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
| function concatArrayBuffer(arrayBufferArray) { let totalLength = 0; arrayBufferArray.forEach(arrayBuffer => { totalLength += arrayBuffer.byteLength; }); const result = new Uint8Array(totalLength); let offset = 0; arrayBufferArray.forEach(arrayBuffer => { result.set(new Uint8Array(arrayBuffer), offset); offset += arrayBuffer.byteLength; }); return result; } if(i + len === sizehigh){ let res = concatArrayBuffer(arrayBufferArray) const blob = await new Blob([res], {type: item.extend}) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.download = item.fileName a.href = url document.body.appendChild(a) a.click() document.body.removeChild(a) ElNotification({ type: 'success', title: '获得了一个文件🎉', message: '下载成功🥳' }) arraymap.delete(index) resetValue(index) }
|
- 如果中途停止下载, 利用一个map对象存储对应index 的 已下载的数据
1 2 3 4
| if(stopdownload.get(index) === true){ arraymap.set(index, arrayBufferArray) return }
|