Skip to content

文件存储


文件上传

java
@RestController
@RequestMapping("/api/files")
public class FileController {

    @Value("${upload.path:/tmp}")
    private String uploadPath;                       // 上传目录,可从配置读取

    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) {
        // 类比 PHP 的 $request->file('file')

        String originalName = file.getOriginalFilename();   // 原始文件名
        String contentType = file.getContentType();          // MIME 类型
        long size = file.getSize();                          // 文件大小(字节)

        // 生成唯一文件名
        String ext = originalName.substring(originalName.lastIndexOf("."));
        String savedName = UUID.randomUUID() + ext;

        try {
            // 保存到本地
            file.transferTo(new File(uploadPath + "/" + savedName));
            return savedName;
        } catch (IOException e) {
            throw new RuntimeException("文件上传失败", e);
        }
    }

    @PostMapping("/uploads")
    public List<String> uploadMultiple(
            @RequestParam("files") List<MultipartFile> files) {  // 多文件
        List<String> names = new ArrayList<>();
        for (MultipartFile file : files) {
            // ... 保存每个文件
        }
        return names;
    }
}

PHP 中 $request->file('avatar')->store('avatars') 一行完成。Java 需要手动处理文件名、保存路径、异常。

原因:Laravel 的文件系统是高度封装的——本地用 storage/app/,云存储用 S3,API 统一。Spring Boot 的 MultipartFile 是原始接口,没有 Laravel 那种 Filesystem 抽象层。


文件下载

java
@GetMapping("/download/{name}")
public ResponseEntity<Resource> download(@PathVariable String name) {
    try {
        Path file = Paths.get(uploadPath).resolve(name);
        Resource resource = new UrlResource(file.toUri());

        if (!resource.exists() || !resource.isReadable()) {
            return ResponseEntity.notFound().build();
        }

        return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .header(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + resource.getFilename() + "\"")
            .body(resource);
    } catch (IOException e) {
        return ResponseEntity.internalServerError().build();
    }
}

静态资源

Spring Boot 默认将以下目录映射为静态资源路径:

src/main/resources/static/     → http://localhost:8080/css/app.css
src/main/resources/public/     → http://localhost:8080/js/app.js
src/main/resources/resources/  → http://localhost:8080/images/logo.png

类比 Laravel 的 public/ 目录。Spring Boot 中叫 static/,内容一样——放 CSS、JS、图片等。

yaml
# 自定义静态资源路径
spring:
  web:
    resources:
      static-locations: classpath:/static/, file:/opt/assets/
      # ⬆ 可以是 classpath 或文件系统路径

⚠️ 常见坑

1. Multipart 文件大小限制

默认最大 1MB,超过报错:

MaxUploadSizeExceededException
yaml
spring:
  servlet:
    multipart:
      max-file-size: 10MB        # 单个文件大小
      max-request-size: 50MB     # 单次请求总大小

2. 文件路径分隔符

java
// Windows 下 File.separator 是 \,Linux 是 /
// 用 Paths.get() 自动处理
Path path = Paths.get(uploadPath, savedName);  // ✅ 跨平台安全

// 不要手动拼接字符串
File f = new File(uploadPath + "/" + savedName);  // ❌ Windows 下可能出问题

3. 临时文件删除

MultipartFile.transferTo() 完成后,临时文件会自动删除。如果你在 transferTo() 之前读了文件内容,需要自己确保临时目录的清理。

4. 没有 Laravel 的 Filesystem 抽象

Laravel 的 Storage::disk('s3')->put(...) 可以无缝切换本地和云存储。Spring Boot 没有这样的内置抽象,需要自己封装或使用第三方库(如 AWS SDK)。

如果只是本地存储,直接用 MultipartFile.transferTo() 就够。如果需要 S3,加上 spring-cloud-starter-aws 依赖。

面向 PHP 开发者的 Spring Boot 文档