-->

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):
image

这里主要是将服务器实际地址与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

posted @ 2022-11-02 22:17  ꧁ʚ星月天空ɞ꧂  阅读(220)  评论(0)    收藏  举报