html5 java文件上传_Vue.Js(html5) + Java实现文件分片上传

说明

代码从项目中剥离修改,未经测试,仅提供思路。

前端

upload(file) {

//从后台获取已经上传的文件分片数

getIdx(md5)

.then(function(res) {

let retry = 3;

uploadPart(retry, file, res.data);

})

.catch();

}

uploadPart(retry, file, idx) {

//设置分片大小(单位Byte)

let bufferLength = 1024 * 1024 * 5;

//计算开始的切割点,idx是上传成功的分片数,未上传过文件则开始点为0

let start = idx * bufferLength;

//全部上传完毕或重试次数用完则退出

if(start>=file.size || retry<=0) return;

//计算分割的位置

let end = start + bufferLength;

//如果分割点超出文件大小,回退分割点

if (end > file.size) {end = fileSize;}

//切割文件

var chunk = file.slice(start, end);

//创建 formData 对象并添加数据

let formData = new FormData();

formData.set("file", chunk);

//如果是第一次上传,连同文件块数量也上传

if (start == 0) {

//计算文件切片总数,向上取整

let chunkNum = Math.ceil(file.size / bufferLength);

formData.set("total", chunkNum);

}

//上传文件的api,此处使用axios发送请求

doUpload(formData)

//发送成功,则上传下一片,递归调用方法

.then(function() {

retry = xx;//刷新重试次数

uploadPart(retry, file, ++idx);

})

//发送失败

.catch(function() {

retry--;//重试次数减一

//重试上传这一片

uploadPart(retry, file, idx);

});

},

文件分片上传的前端关键代码只有一句:

//切割文件

var chunk = file.slice(start, end);

通过slice方法来切割文件,然后文件上传的流程视业务和具体技术而定,此处是使用axios发送请求,用递归调用上传文件块。

需要注意的是,Blob.slice(start, end),文件块包含start指向的字节,而不包含end指向的字节,在使用时要注意Blob的边界。

后端

/**合并文件的实际操作*/

public static void doMergeFiles(String outFile, String[] files) {

//设置缓存大小

int BUFSIZE = 1024 * 1024;

//排序。文件后缀名是文件的顺序。

Arrays.sort(files);

//输出流

FileChannel outChannel = null;

//标记最后的一个文件

String lastFlag = files[files.length-1];

try {

outChannel = new FileOutputStream(outFile).getChannel();

//遍历文件列表

for(String f : files){

//最后一块文件用真实大小设置缓存,避免自动填充数据造成的md5不一致

if(lastFlag.equals(f)){

File last = new File(f);

BUFSIZE = (int) last.length();//获取文件的大小并设置成缓存的大小

}

FileChannel fc = new FileInputStream(f).getChannel();

//用ByteBuffer创建缓存

ByteBuffer bb = ByteBuffer.allocate(BUFSIZE);

while(fc.read(bb) != -1){//把数据读到缓存

bb.flip();//重置游标

outChannel.write(bb);//写入数据

bb.clear();//清空数据

}

fc.close();//关闭流

}

} catch (IOException ioe) {

ioe.printStackTrace();

} finally {

try {if (outChannel != null) {outChannel.close();}} catch (IOException ignore) {}

}

}

后端的关键是合并文件,当上传完最后一块文件就进行文件的合并。使用ByteBuffer缓存,使用FileChannel进行文件的读写完成合并操作。在保存文件时,文件名取一致,文件的后缀名则取文件块的顺序,比如第一块文件是“xxx.01”,第10块是“xxx.10”,注意,个位数前面要补“0”,这样可以直接用Array.sort()进行排序。

为提高性能,可以适当设置缓存大小,可以边上传文件边合并,不必等到文件都上传了才合并。

拓展

此处的文件上传是一次上传一片,上传成功才开始上传下一片。如果前端不是使用javascript,能开启使用多线程的话,可以改成同时上传多片文件提高上传速度。已经上传的文件分片用bitmap存储,上传文件前,从后台获取已上传的文件分片的bitmap数据然后解析,多线程处理未上传的文件分片。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring MVC + Vue.js 的大文件分片上传前后端MD5加密流程如下: 1. 前端通过 Vue.js 实现文件分片上传,同时计算每个分片的MD5值。 ```javascript <template> <div> <input type="file" @change="uploadFile"/> </div> </template> <script> import SparkMD5 from 'spark-md5' export default { name: 'FileUpload', methods: { async uploadFile(event) { const file = event.target.files[0] const chunkSize = 2097152 // 每个分片的大小为2MB const chunks = Math.ceil(file.size / chunkSize) // 文件分成的总块数 const fileReader = new FileReader() const spark = new SparkMD5.ArrayBuffer() const md5List = [] for (let i = 0; i < chunks; i++) { const start = i * chunkSize const end = start + chunkSize > file.size ? file.size : start + chunkSize const chunk = file.slice(start, end) const chunkReader = new FileReader() chunkReader.onload = e => { spark.append(e.target.result) md5List.push(spark.end()) } chunkReader.readAsArrayBuffer(chunk) } fileReader.onload = e => { const formData = new FormData() formData.append('file', file) formData.append('md5List', JSON.stringify(md5List)) // 发送文件分片和MD5值到后端 this.$axios.post('/uploadFile', formData) } fileReader.readAsArrayBuffer(file) } } } </script> ``` 2. 后端 Spring MVC 接收到文件分片和 MD5 值,对每个分片进行 MD5 计算,最后将所有分片的 MD5 值进行合并,得到整个文件的 MD5 值,并与前端传过来的 MD5 值进行比较,如果相同,则说明文件传输完整无误。 ```java import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.List; @RestController public class FileUploadController { @PostMapping("/uploadFile") public void uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("md5List") String md5List, HttpServletRequest request) throws IOException, NoSuchAlgorithmException { // 将 MD5 值的 JSON 字符串转换成 List List<String> md5Array = JSON.parseArray(md5List, String.class); // 计算文件的 MD5 值 MessageDigest md5Digest = MessageDigest.getInstance("MD5"); byte[] buffer = new byte[1024 * 1024]; int len; try (RandomAccessFile randomAccessFile = new RandomAccessFile(file.getOriginalFilename(), "rw")) { for (int i = 0; i < md5Array.size(); i++) { randomAccessFile.seek(i * buffer.length); len = randomAccessFile.read(buffer, 0, buffer.length); md5Digest.update(buffer, 0, len); if (!md5Array.get(i).equals(getMD5(buffer, len))) { throw new IOException("文件传输错误"); } } randomAccessFile.seek(0); while ((len = file.getInputStream().read(buffer)) != -1) { randomAccessFile.write(buffer, 0, len); } } // 保存文件 File newFile = new File("upload/" + file.getOriginalFilename()); file.transferTo(newFile); } private String getMD5(byte[] buffer, int len) throws NoSuchAlgorithmException { MessageDigest md5Digest = MessageDigest.getInstance("MD5"); md5Digest.update(buffer, 0, len); byte[] md5Bytes = md5Digest.digest(); StringBuilder stringBuilder = new StringBuilder(); for (byte md5Byte : md5Bytes) { int value = md5Byte & 0xFF; if (value < 16) { stringBuilder.append("0"); } stringBuilder.append(Integer.toHexString(value)); } return stringBuilder.toString(); } } ``` 以上代码仅供参考,实际应用中需要根据具体需求进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值