【Spring Content】Spring Content fs(本地文件系统)的文件上传下载

42 阅读2分钟

前置准备

  • 环境:JDK17
  • SpringBoot:SpringBoot3
  • SpringContent:3.0.9

依赖引入

注意:fs(本地文件系统)和S3的starter只能设置1个,否则启动时会报错!

引入fs的stater

<dependency>
    <groupId>com.github.paulcwarren</groupId>
    <artifactId>spring-content-fs-boot-starter</artifactId>
    <version>3.0.9</version>
</dependency>

实现

开启SpringContent包扫描

启动类添加@EnableFilesystemStores注解,标记要扫描的包

@EnableFilesystemStores(basePackages = "org.evaltool.*")

定义文件实体对象

  • @ContentId:存储文件时的唯一编号
  • @ContentLength:文件的大小
import lombok.Data;
import org.springframework.content.commons.annotations.ContentId;
import org.springframework.content.commons.annotations.ContentLength;

/**
 * spring content文件定义
 */
@Data
public class FileDocument {
    @ContentId
    private String id;
    private String fileName;
    @ContentLength
    private long fileSize;
    private String fileExt;
}

定义Store接口

自定义接口继承自ContentStore。

ContentStore 是 Spring Content 中的核心接口,用于定义内容存储和检索的标准操作。它提供了一套统一的 API 来处理各种类型的内容存储后端。

import org.evaltool.tool.domain.FileDocument;
import org.springframework.content.commons.store.ContentStore;
import org.springframework.stereotype.Repository;

/**
 * 本地文件存储器
 */
@Repository
public interface LocalFileStore extends ContentStore<FileDocument, String> {
}

AppService中使用Store

import lombok.extern.slf4j.Slf4j;
import org.evaltool.evalengine.common.utils.NanoIdUtils;
import org.evaltool.tool.domain.FileDocument;
import org.evaltool.tool.store.LocalFileStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;

@Slf4j
@Service
public class FileStorageAppService {
    @Autowired
    private LocalFileStore fileStore;

    /**
     * 上传文件
     */
    public String storeFile(MultipartFile file) {
        String original = file.getOriginalFilename();
        String ext = StringUtils.getFilenameExtension(original);
        if (original == null || original.contains(".."))
            throw new RuntimeException("非法文件名");
        FileDocument doc = new FileDocument();
        String nanoId = NanoIdUtils.random();
        doc.setId(nanoId);
        doc.setFileName(original);
        doc.setFileExt(ext);
        doc.setFileSize(file.getSize());
        try (InputStream in = file.getInputStream()) {
            fileStore.setContent(doc, in);
        } catch (IOException e) {
            throw new RuntimeException("存储文件失败", e);
        }
        return doc.getId() + "." + ext;
    }

    /**
     * 下载文件
     */
    public Resource loadFile(String fileName) {
        String ext = StringUtils.getFilenameExtension(fileName);
        ext = ext != null ? ext : "";
        String fileId = StringUtils.replace(fileName, "." + ext, "");
        FileDocument doc = new FileDocument();
        doc.setId(fileId);
        doc.setFileName(fileName);
        doc.setFileExt(ext);
        InputStream content = fileStore.getContent(doc);
        if (content == null) {
            throw new IllegalArgumentException("File not found");
        }
        return new InputStreamResource(content);
    }
}

(可选操作)保存的文件携带后缀

SpringContent默认存储的文件名等于id,不带后缀。如果要携带后缀,需要修改配置,添加Converter来增加后缀。

注意:本地文件只能使用filesystemStorePlacementService

import org.apache.commons.lang3.StringUtils;
import org.evaltool.tool.domain.FileDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.ConverterRegistry;

@Configuration
public class ContentStoreConfig {
    /**
     * Spring Content默认使用id作为文件名不满足要求, 需要给文件添加扩展名
     */
    @Autowired
    public void configureConverter(@Qualifier("filesystemStorePlacementService") ConverterRegistry registry) {
        registry.addConverter(FileDocument.class, String.class, doc -> {
            String ext = doc.getFileExt();
            return StringUtils.isEmpty(ext) ? doc.getId() : doc.getId() + "." + ext.toLowerCase();
        });
    }
}