springboot实现上传文件与下载文件
一、 上传文件
1. 概述
springboot实现文件的上传,主要分为2种方式:1、通过数据库存取文件的url实现存取与读取2、通过数据库存取文件进行操作,下面我将主要讲述方式1
2. 代码实现
yml配置文件
file:
# 本地windows
#location: E:/idea-download/
#linux服务器
location: /usr/local/apps/pmxt-download/
path: /download/**
# domainname: http://43.142.87.68:9001
domainname: http://localhost:9001
location为服务器实际存储地址,path为通过URL地址的访问路径,两者是对应的
配置文件
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class FileDownloadConfig implements WebMvcConfigurer {
@Value("${file.location}")
String filelocation;
@Value("${file.path}")
String filepath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//匹配到resourceHandler,将URL映射至location,也就是本地文件夹
registry.addResourceHandler(filepath).addResourceLocations("file:///" + filelocation);//这里最后一个/不能不写
}
}
上述的配置文件也可以通过yml实现,如下图(web上级为spring):

这里主要是将服务器实际地址与URL对应
Controller
import com.example.pmxt.common.ReturnResult;
import com.example.pmxt.domain.Auctioneer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("/auctioneers")
@Validated
public class AuctioneerController {
//获取yml里配置好的变量
@Value("${file.location}")
String filelocation;
@Value("${file.path}")
String filepath;
@Value("${file.domainname}")
String domainname;
private final AuctioneerService auctioneerService;
@Autowired
public AuctioneerController(AuctioneerService auctioneerService) {
this.auctioneerService = auctioneerService;
}
@GetMapping()
public ReturnResult getauctioneerall(){
return ReturnResult.buildSuccessResult(auctioneerService.list());
}
@GetMapping("/{pmsbh}")
public ReturnResult getbypmsbh(@PathVariable @Validated String pmsbh){
Map<String,Object> map = new HashMap<>();
map.put("pmsbh",pmsbh);
return ReturnResult.buildSuccessResult(auctioneerService.listByMap(map));
}
@PostMapping()
public ReturnResult addauctioneer(@RequestBody @Validated Auctioneer auctioneer){
try {
// 获取文件名
String FileName = auctioneer.getPmszsz().substring(auctioneer.getPmszsz().lastIndexOf("/"));
// 复制文件
Files.copy(Paths.get(filelocation + "/auctioneer/temp/" + FileName), Paths.get(filelocation + "/auctioneer/" + FileName));
// 删除临时文件
Files.delete(Paths.get(filelocation + "/auctioneer/temp/" + FileName));
// 保存新的url
auctioneer.setPmszsz(auctioneer.getPmszsz().replace("/temp",""));
// 存入数据库
if(auctioneerService.save(auctioneer)){
return ReturnResult.buildSuccessResult(1);
}
else{
return ReturnResult.buildSuccessResult(0);
}
}
catch (IOException e){
return ReturnResult.buildFailureResult("logo/officialSeal 文件url不正确");
}
}
@DeleteMapping("/{id}")
public ReturnResult deletebyid(@PathVariable @Validated Integer id){
return ReturnResult.buildSuccessResult(auctioneerService.removeById(id));
}
@PutMapping()
public ReturnResult updateauctioneer(@RequestBody @Validated Auctioneer auctioneer){
return ReturnResult.buildSuccessResult(auctioneerService.updateById(auctioneer));
}
@PostMapping("/addpmszhz")
public ReturnResult addCarousel(MultipartFile file) {
if (file == null || file.isEmpty()) {
return ReturnResult.buildFailureResult("请传入文件");
} else {
try {
// 保存图片
// 服务器文件名
String serverFilename = UUID.randomUUID().toString().substring(0, 8)
+ file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
// 创建目录、存储图片地址目录
String path = filelocation + "/auctioneer/temp/";
// 存储图片的url
Files.createDirectories(Paths.get(path));
// 保存
file.transferTo(Path.of(path, serverFilename));
//这里是将URL返回给了前端,实际上也可以传到数据库,不过那样就其他的值也要一起传进来
return ReturnResult.buildSuccessResult(domainname + "/download/auctioneer/temp/" + serverFilename);
} catch (Exception e) {
e.printStackTrace();
return ReturnResult.buildFailureResult(e.toString());
}
}
}
}
二、下载文件
概述
一般来讲对于文件的下载只要给前端文件地址就好了,但是在修改jeecgboot框架时,发现文件只有PDF与图片格式能够打开并下载,而word与excel格式则无法打开下载,后来仔细查了一下,因为浏览器自带PDF与图片浏览插件,所以能够查看下载,而word与excel则只能下载,但是总是报401,token失效,后来仔细查了一下是因为前端把jeecgboot文件下载地址改了,由于没有在特定的地址下,所以受到了拦截器,而且token失效,但是当时以防万一自己也是写了个下载接口
方式一:下载到本地
导入依赖
// https://mvnrepository.com/artifact/commons-io/commons-io
implementation 'commons-io:commons-io:2.11.0'
写定下载静态方法
public static void downloadByCommonIO(String url, String saveDir, String fileName) {
try {
FileUtils.copyURLToFile(new URL(url), new File(saveDir, fileName));
} catch (IOException e) {
e.printStackTrace();
}
}
Controller接口
@PostMapping("/download")
public ReturnResult downloadFile(){
downloadByCommonIO("http://101.42.99.134:8888/jeecg-boot/sys/common/static/temp/46f630ac.xlsx","E:/桌面/新建文件夹","test.xlsx");
return ReturnResult.buildSuccessResult(true);
}
特点:
- 用于从URL下载文件并保存到本地
- 主要用于网络资源下载到本地文件
- 是将远程资源复制到本地文件的操作
- 不适用于直接HTTP响应下载
方式二:文件流下载
服务层
@Service
public class FileDownloadService {
@Value("${file.download.base-path}")
private String baseDownloadPath;
/**
* 下载文件核心逻辑
*/
public void downloadFile(String filePath, HttpServletResponse response) throws IOException {
// 1. 验证文件路径安全性
String safeFilePath = validateAndSanitizePath(filePath);
// 2. 检查文件是否存在
File file = new File(safeFilePath);
if (!file.exists()) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 3. 设置响应头
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
response.setContentLengthLong(file.length());
// 4. 输出文件内容
try (InputStream inputStream = new FileInputStream(file);
OutputStream outputStream = response.getOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
}
/**
* 验证和清理文件路径
*/
private String validateAndSanitizePath(String filePath) throws IOException {
// 防止路径遍历攻击
String normalizedPath = Paths.get(baseDownloadPath, filePath).normalize().toString();
if (!normalizedPath.startsWith(baseDownloadPath)) {
throw new SecurityException("非法文件路径访问");
}
return normalizedPath;
}
}
这里易出现前端无法下载的问题,注意response.setHeader里要加上Content-Disposition和Access-Control-Expose-Headers
控制器
@RestController
@RequestMapping("/api/file")
public class FileDownloadController {
@Autowired
private FileDownloadService fileDownloadService;
/**
* 下载指定路径的文件
*/
@GetMapping("/download")
public void downloadFile(@RequestParam String filePath,
HttpServletResponse response) throws IOException {
fileDownloadService.downloadFile(filePath, response);
}
}
特点:
- 直接从本地文件系统读取文件
- 手动控制文件读取和写入过程
- 数据直接从文件流向HTTP响应流
- 适用于本地文件下载场景
主要区别
| 特性 | 直接文件流 | FileUtils.copyURLToFile |
|---|---|---|
| 用途 | 本地文件直接响应给客户端 | URL资源下载到本地文件 |
| 数据流向 | 文件 → HTTP响应流 | URL → 本地文件 |
| 适用场景 | 文件下载接口 | 网络资源本地缓存 |
| 性能 | 直接流传输,效率高 | 需要中间文件存储 |
参考文档:https://blog.csdn.net/m0_46360888/article/details/128022927

浙公网安备 33010602011771号