Springboot+文件上传下载问题
需要hutool的工具 hutool可以生成数据md5等一些工具 非常好用
依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
如过想传输比较大的数据还需要我们配置相应的依赖
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
文件传输的总体逻辑
首先我们需要一个测试工具postman 上传文件给服务端(springboot) 具体大家可以搜一下如何使用
我们的数据都是存储在服务器磁盘上的。当我们把数据存储到服务器的磁盘上就完成了上传文件的过程。
再从服务器的磁盘上下载文件就完成了下载过程。
我们下载文件的过程中 需要获取文件在服务器的名字 等信息 来定位文件在服务器中的位置。
所以我们需要数据库(Mysql)来在逻辑上 存储文件的一些信息。拿到文件的逻辑信息(下载链接后)
就可以通过请求接口 来通过网络流传输到自己的本地。
数据库
大概需要这样的一些信息 来逻辑存储文件的一些属性。
当然后台也需要有controller entity mapper service等 这些是基础 我就不一一展示
上传文件
在Yml简单定义一下下载的地址:
files:
upload:
path: D:/learnjava/MyAitest/files/
在这里使用该地址
@Value("${files.upload.path}")
private String fileuploadPath;
upload 代码的总体逻辑就是
1.我们从本地上传文件 服务器(springboot)获取到
原始的文件名(我们本地的文件名)获取文件类型(后缀 例如.txt) 获取文件的大小 这些都需要后面存储到数据库保留数据信息
这些使用一些FileUtil工具可以实现
2.判断目录是否存在 如果不存在则创建这个目录
使用IdUtil随机生成文件名
再加上原来文件的后缀形成最终我们需要存储到服务器的位置
File uploadFile= new File(fileuploadPath+uuid+ StrUtil.DOT+type);//最终存储的位置
然后存储到磁盘
3.另外每个文件内容转换成md5后 都是唯一的。利用这个特性
我们获取到当前上传文件的md5 然后再在数据库中查找是否存在这个md5如果已经存在 那么该文件的下载地址就和它相同md5的那个文件的下载地址是一样的 并且删除掉这个文件不让它存在磁盘
这样可以避免磁盘空间的浪费
String md5= SecureUtil.md5(uploadFile);//工具获取起md5
Files dbFiles=getFileByMd5(md5);
if(dbFiles!=null){
url=dbFiles.getUrl();
uploadFile.delete();
}
else{
url="http://localhost:9090/file/"+fileuuid;//上传成功返回属性
}
4.最后把文件信息保存在数据库
总结一下:每次上传一个文件 数据库多一条数据 但是磁盘不一定多一条数据 有可能会删除
下载文件
1.有刚才的fileuploadPath+接口请求传来的文件名+后缀
定位到需要下载的文件
@GetMapping("/{fileUUID}")
File loadFile = new File(fileuploadPath+fileUUID);//把文件获取到
2.需要我们设置返回的header 和contentType
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(fileUUID,"UTF-8"));
3.把文件转换成字节流
通过HTTP的setvletoutstream通道 把数据输出回去 就完成了下载操作
ServletOutputStream out=response.getOutputStream();//获取返回数据的通道 写数据
byte[] bytes = FileUtil.readBytes(loadFile);//把文件拆解成 字节- 字节的形式
out.write(bytes);//写出数据
//对网络传输头 内容 流 文件进行处理
out.flush();//刷新
out.close();//关闭
上传和下载的全部controller代码
package com.hxt.myaitest.controller;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hxt.myaitest.entity.Files;
import com.hxt.myaitest.service.IFileService;
import org.apache.xmlbeans.impl.xb.ltgfmt.TestCase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.net.URLEncoder;
/**
*
*
* 文件上传接口
*/
@RestController
@RequestMapping("/file")
public class FileController {
@Autowired
private IFileService fileService;
@Value("${files.upload.path}")
private String fileuploadPath;
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) throws Exception {
String originalFilename = file.getOriginalFilename();//获取文件的名字
String type= FileUtil.extName(originalFilename);//获取文件的类型//后缀
long size=file.getSize();//获取文件的大小
//先存储到磁盘
//再存储到数据库
File uploadParentFile = new File(fileuploadPath);//原始目录的地址
if (!uploadParentFile.exists()) {
uploadParentFile.mkdirs();
}//需要存在否则创建一个目录
String uuid= IdUtil.fastSimpleUUID();//随机生成文件名标识
String fileuuid=uuid+StrUtil.DOT+type;//文件名+后缀(类型) 形成最后的UUID
File uploadFile= new File(fileuploadPath+uuid+ StrUtil.DOT+type);//最终存储的位置
//存储磁盘
file.transferTo(uploadFile);//存储磁盘
String url;
//保证重复的文件不会都读到磁盘里面占用空间
String md5= SecureUtil.md5(uploadFile);//工具获取起md5
Files dbFiles=getFileByMd5(md5);
if(dbFiles!=null){
url=dbFiles.getUrl();
uploadFile.delete();
}
else{
url="http://localhost:9090/file/"+fileuuid;//上传成功返回属性
}
//把重要信息 文件名字 类型和大小存储到数据库
Files savefile=new Files();//java里面的实体file 而不是自带的文件系统
savefile.setMd5(md5);
savefile.setName(originalFilename);
savefile.setType(type);
savefile.setSize(size/1024);//把B转换成KB
savefile.setUrl(url);
fileService.save(savefile);//保存到数据库
return url;
}
@GetMapping("/{fileUUID}")
public void download(@PathVariable String fileUUID, HttpServletResponse response) throws Exception {
File loadFile = new File(fileuploadPath+fileUUID);//把文件获取到
//对网络传输头 内容 流 文件进行处理
ServletOutputStream out=response.getOutputStream();//获取返回数据的通道 写数据
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(fileUUID,"UTF-8"));
byte[] bytes = FileUtil.readBytes(loadFile);//把文件拆解成 字节- 字节的形式
out.write(bytes);//写出数据
//对网络传输头 内容 流 文件进行处理
out.flush();//刷新
out.close();//关闭
}
private Files getFileByMd5(String md5) {
QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("md5", md5);
return fileService.getBaseMapper().selectOne(queryWrapper);
}
}