1.前端代码:
<template>
<div>
<div class="container">
<el-upload
action
class="upload-demo"
drag
multiple
:http-request="httpRequestHandle"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或
<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">只能上传 jpg/png 文件,且不超过 500kb</div>
</template>
</el-upload>
</div>
</div>
</template>
<script>
import SparkMD5 from 'spark-md5'
import ajax from 'axios';
export default {
name: "upload",
data() {
return {
chunks: 0,
chunkSize: 1024 * 1024,
};
},
methods: {
// 修改时间+文件名称+最后修改时间-->MD5
md5File(file) {
return new Promise((resolve, reject) => {
let blobSlice =
File.prototype.slice ||
File.prototype.mozSlice ||
File.prototype.webkitSlice
let chunkSize = file.size / 100
let chunks = 100
let currentChunk = 0
let spark = new SparkMD5.ArrayBuffer()
let fileReader = new FileReader()
fileReader.onload = function (e) {
console.log('read chunk nr', currentChunk + 1, 'of', chunks)
spark.append(e.target.result) // Append array buffer
currentChunk++
if (currentChunk < chunks) {
loadNext()
} else {
let cur = +new Date()
let result = spark.end()
resolve(result)
}
}
fileReader.onerror = function (err) {
reject(err)
}
function loadNext() {
let start = currentChunk * chunkSize
let end = start + chunkSize >= file.size ? file.size : start + chunkSize
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
}
loadNext()
})
},
// 校验文件的MD5
checkFileMD5(file, fileName, fileMd5Value, onError) {
const fileSize = file.size
const {chunkSize} = this
this.chunks = Math.ceil(fileSize / chunkSize)
return new Promise(async (resolve, reject) => {
let formData = new FormData();
formData.append("fileName", fileName);
formData.append("path", fileMd5Value);
const result = await ajax({
url: 'common/upload/checkFile',
method: "POST",
baseURL: "http://localhost:8080",
data: formData
})
resolve(result)
})
},
//文件分片
async checkAndUploadChunk(file, fileMd5Value, chunkList) {
let {chunks, upload} = this
const requestList = []
for (let i = 0; i < chunks; i++) {
requestList.push(upload(i, fileMd5Value, file))
}
console.log({requestList})
const result =
requestList.length > 0
? await Promise.all(requestList)
.then(result => {
console.log({result})
//return result.every(i => i.ok)
return true;
})
.catch(err => {
return err
})
: true
console.log({result})
return result === true
},
// 上传chunk
upload(i, fileMd5Value, file) {
const {chunks} = this
return new Promise((resolve, reject) => {
let {chunkSize} = this
// 构造一个表单,FormData是HTML5新增的
let end = (i + 1) * chunkSize >= file.size ? file.size : (i + 1) * chunkSize
let form = new FormData()
form.append('file', file.slice(i * chunkSize, end)) // file对象的slice方法用于切出文件的一部分
form.append('total', chunks) // 总片数
form.append('index', i) // 当前是第几片
form.append('path', fileMd5Value)
ajax({
url: 'common/upload/uploadFile',
method: "POST",
baseURL: "http://localhost:8080",
data: form
}).then(data => {
resolve(data)
})
})
},
async httpRequestHandle(data) {
let fileMd5Value = await this.md5File(data.file)
let uploaded = await this.checkFileMD5(data.file, data.file.name, fileMd5Value)
await this.checkAndUploadChunk(data.file, fileMd5Value);
var formData = new FormData();
formData.append("name", data.file.name);
formData.append("path", fileMd5Value);
ajax({
url: 'common/upload/mergeFile',
method: "POST",
baseURL: "http://localhost:8080",
data: formData
})
}
}
};
</script>
2.后端代码:
package com.lz.hehuorenservice.controller.common;
import com.lz.hehuorenservice.common.bean.Project;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
/** Create by hyhweb on 2021/6/1 18:01 */
@RestController
@Api(tags = "上传接口")
@RequestMapping("common")
public class UploadController {
@Autowired
private Project project;
@ApiOperation("上传图片接口")
@PostMapping("/upload/uploadFile")
public String upload(
@RequestParam("file") MultipartFile file,
@RequestParam("path") String path,
@RequestParam("index") String index,
@RequestParam("total") String total)
throws Exception {
if (file.isEmpty()) {
return "请选择要上传的文件";
}
byte[] bytes = file.getBytes();
String fileName = file.getOriginalFilename();
Path uploadPath = Paths.get(project.getUploadUrl() + path + "/" + index);
File files = new File(project.getUploadUrl() + path);
if (!files.exists()) {
files.mkdirs();
}
Files.write(uploadPath, bytes);
return "上传成功";
}
@PostMapping("/upload/checkFile")
public Map checkFile(@RequestParam("path") String path) throws Exception {
String uploadPath = project.getUploadUrl() + path;
File file = new File(uploadPath);
if (file.exists() && file.isDirectory()) {
String[] fileList = file.list();
Map map = new HashMap();
map.put("list", fileList);
map.put("message", "文件已经存在");
return map;
} else {
Map map = new HashMap();
file.mkdirs();
map.put("message", "文件不存在,已经重新创建");
return map;
}
}
@PostMapping("/upload/mergeFile")
public String mergeFile(@RequestParam("path") String path, @RequestParam("name") String name)
throws Exception {
String uploadPath = project.getUploadUrl() + path;
File file = new File(uploadPath);
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (file.exists()) {
String[] fileList = file.list();
int lengthByte = 0;
for (String url : fileList) {
InputStream in = new FileInputStream(uploadPath + "/" + url);
lengthByte += in.available();
}
byte[] bytes = new byte[lengthByte];
int countLength = 0;
for (String url : fileList) {
InputStream in = new FileInputStream(uploadPath + "/" + url);
byte[] b = toByteArray(in);
System.arraycopy(b, 0, bytes, countLength, b.length);
countLength += b.length;
in.close();
}
Path savePath = Paths.get(uploadPath + "-" + name);
Files.write(savePath, bytes);
}
return "合并文件成功";
}
private static byte[] byteMerger(byte[]... byteList) {
int lengthByte = 0;
for (int i = 0; i < byteList.length; i++) {
lengthByte += byteList[i].length;
}
byte[] allByte = new byte[lengthByte];
int countLength = 0;
for (int i = 0; i < byteList.length; i++) {
byte[] b = byteList[i];
System.arraycopy(b, 0, allByte, countLength, b.length);
countLength += b.length;
}
return allByte;
}
private byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 1024];
int n = 0;
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
}
3.实现效果,如下:
先分片上传文件,最后合并成一个文件
上传到服务器的分片文件,不需要文件后缀,到时候以流的方式读取出来合并文件就好
文件打开正常,如下: