java 文件分块上传_采用前后端分离的方式进行开发,实现了几种常用的文件上传功能...

MyUploader-Backend

单文件上传,多文件上传,大文件上传,断点续传,文件秒传,图片上传

简介

采用前后端分离的方式进行开发,实现了几种常用的文件上传功能。 前端采用 vue.js + plupload + element-ui 实现了文件在浏览器端的发送, 后端采用 spring boot + spring + spring mvc + mybatis 实现了文件在服务器端的接收和存储。

本项目为后端实现

效果图

单文件上传

45d874fce93502fe4e8d1ba21bf2a6f8.gif

多文件上传

b51cdf1f05803dac9295eab6359751aa.gif

大文件上传

9373fdb1b192f0d49ed401dd7a3a19bb.gif

断点续传

74848247e25678e343ed733852f14353.gif

文件秒传

9a8a1e65c7b3b3da50fc4754b4be6262.gif

图片上传

d88f095b9bc050d71953eacf5349a4a1.gif

后端实现

未分块上传

采用MultipartFile接收上传文件并使用FileOutputStream写入文件

/**

* 写入文件

* @param target

* @param src

* @throws IOException

*/

public static void write(String target, InputStream src) throws IOException {

OutputStream os = new FileOutputStream(target);

byte[] buf = new byte[1024];

int len;

while (-1 != (len = src.read(buf))) {

os.write(buf,0,len);

}

os.flush();

os.close();

}

分块上传

采用MultipartFile接收上传文件的分块文件,上传参数包括:size:文件大小,chunk:分块号,chunks:总分块数

@PostMapping("/")

public void upload(String name,

String md5,

Long size,

Integer chunks,

Integer chunk,

MultipartFile file) throws IOException {

if (chunks != null && chunks != 0) {

fileService.uploadWithBlock(name, md5,size,chunks,chunk,file);

} else {

fileService.upload(name, md5,file);

}

}

分块上传信息的存储

使用Map来存储分块的上传信息,key为文件唯一标识由前端发送这里采用文件MD5作为文件的唯一标识,value为自定义的静态内部类,其中name属性为文件在服务器端存储的随机文件名其在该文件的第一个分块上传到服务器时随机生成(UUID),boolean数组记录了分块的上传情况已上传置为true。

/**

* 分块上传工具类

*/

public class UploadUtils {

/**

* 内部类记录分块上传文件信息

*/

private static class Value {

String name;

boolean[] status;

Value(int n) {

this.name = generateFileName();

this.status = new boolean[n];

}

}

private static Map chunkMap = new HashMap<>();

/**

* 判断文件所有分块是否已上传

* @param key

* @return

*/

public static boolean isUploaded(String key) {

if (isExist(key)) {

for (boolean b : chunkMap.get(key).status) {

if (!b) {

return false;

}

}

return true;

}

return false;

}

/**

* 判断文件是否有分块已上传

* @param key

* @return

*/

private static boolean isExist(String key) {

return chunkMap.containsKey(key);

}

/**

* 为文件添加上传分块记录

* @param key

* @param chunk

*/

public static void addChunk(String key, int chunk) {

chunkMap.get(key).status[chunk] = true;

}

/**

* 从map中删除键为key的键值对

* @param key

*/

public static void removeKey(String key) {

if (isExist(key)) {

chunkMap.remove(key);

}

}

/**

* 获取随机生成的文件名

* @param key

* @param chunks

* @return

*/

public static String getFileName(String key, int chunks) {

if (!isExist(key)) {

synchronized (UploadUtils.class) {

if (!isExist(key)) {

chunkMap.put(key, new Value(chunks));

}

}

}

return chunkMap.get(key).name;

}

}

分块写入文件

使用RandomAccessFile随机读写文件,通过targetSize指定文件的大小,通过seek定位分块在文件中对应位置,通过write方法进行写入。

/**

* 分块写入文件

* @param target

* @param targetSize

* @param src

* @param srcSize

* @param chunks

* @param chunk

* @throws IOException

*/

public static void writeWithBlok(String target, Long targetSize, InputStream src, Long srcSize, Integer chunks, Integer chunk) throws IOException {

RandomAccessFile randomAccessFile = new RandomAccessFile(target,"rw");

randomAccessFile.setLength(targetSize);

if (chunk == chunks - 1) {

randomAccessFile.seek(targetSize - srcSize);

} else {

randomAccessFile.seek(chunk * srcSize);

}

byte[] buf = new byte[1024];

int len;

while (-1 != (len = src.read(buf))) {

randomAccessFile.write(buf,0,len);

}

randomAccessFile.close();

}

分块上传逻辑

每次分块上传时首先获取对应文件在服务器端生成的随机文件名,如果不存在即在这之前还没有分块上传,则创建文件分块存储信息的键值对并存入map中,同时返回刚生成的随机文件名。然后将分块写入文件,同时将boolean数组中当前对应分块的位置置为true。最后判断该文件的所有分块是否全部上传,如果已全部上传完成则将文件信息写入数据库同时删除map中对应的键值对。

ps: 曾用文件夹存储上传分块,当分块全部上传完成时按顺序合并文件夹内所有分块,然后删除文件夹,后来发现RandomAccessFile果断弃坑。

/**

* 分块上传文件

* @param md5

* @param size

* @param chunks

* @param chunk

* @param file

* @throws IOException

*/

public void uploadWithBlock(String name,

String md5,

Long size,

Integer chunks,

Integer chunk,

MultipartFile file) throws IOException {

String fileName = getFileName(md5, chunks);

FileUtils.writeWithBlok(UploadConfig.path + fileName, size, file.getInputStream(), file.getSize(), chunks, chunk);

addChunk(md5,chunk);

if (isUploaded(md5)) {

removeKey(md5);

fileDao.save(new File(name, md5,UploadConfig.path + fileName, new Date()));

}

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值