Skip to content

文件相关

说明

文件上传直接调用文件服务接口即可, 本地文件下载需要依赖nginx, 文件预览依赖第三方组件 kkFileView

名词解释

  • 热更新:不重启项目,修改nacos中配置的参数后,实时生效

  • 文件服务:本文档中提到的文件服务,你直接把他理解成acuity-base-server中文件相关的逻辑即可、或直接理解为acuity-base-server也行。

上传

文件服务目前支持本地存储、阿里云OSS、FastDFS存储、华为云OBS、MinIO、七牛云OSS,可以通过配置切换。完整的文件存储配置如下:

yaml
# acuity.file 相关的配置会注入到 FileServerProperties 对象,使用时只需要注入即可。
acuity:
  file:
    storageType: MIN_IO   # 存储类型 支持 LOCAL FAST_DFS MIN_IO ALI_OSS HUAWEI_OSS QINIU_OSS
    delFile: false        # 调用删除接口是否删除文件,  设置为false只删除数据库记录
    local:   # 文件存储到服务器本机
      storage-path: /data/projects/uploadfile/file/   # 文件存储到服务器的绝对路径(需要启动项目前创建好,并且需要有读和写的权限!)
      endpoint: http://127.0.0.1/file/   # 外网文件访问前缀 (部署nginx后,配置为nginx的访问地址,并需要在nginx配置文件中静态代理storage-path)
      inner-uri-prefix: null  		# 内网的url访问前缀
    ali:    # 文件存储到阿里云OSS
      # 请填写自己的阿里云存储配置
      uriPrefix: "http://acuity-admin-cloud.oss-cn-beijing.aliyuncs.com/"
      bucket-name: "acuity-admin-cloud"
      endpoint: "oss-cn-beijing.aliyuncs.com"
      access-key-id: "填写你的id"
      access-key-secret: "填写你的秘钥"
    minIo:  # 文件存储到MINIO
      endpoint: "https://127.0.0.1:9000/"
      accessKey: "acuity"
      secretKey: "acuity"
      bucket: "dev"
    huawei:  # 文件存储到华为云OBS
      uriPrefix: "dev.obs.cn-southwest-2.myhuaweicloud.com"
      endpoint: "obs.cn-southwest-2.myhuaweicloud.com"
      accessKey: "1"
      secretKey: "2"
      location: "cn-southwest-2"
      bucket: "dev"
    qiNiu:    # 文件存储到七牛云OSS
      zone: "z0"
      accessKey: "1"
      secretKey: "2"
      bucket: "acuity_admin_cloud"
      
# acuity.file.storageType=FAST_DFS 时,需要配置
fdfs:
  soTimeout: 1500
  connectTimeout: 600
  thumb-image:
    width: 150
    height: 150
  tracker-list:
    - 192.168.1.2:22122
  pool:
    #从池中借出的对象的最大数目
    max-total: 153
    max-wait-millis: 102
    jmx-name-base: 1
    jmx-name-prefix: 1
# acuity.file 相关的配置会注入到 FileServerProperties 对象,使用时只需要注入即可。
acuity:
  file:
    storageType: MIN_IO   # 存储类型 支持 LOCAL FAST_DFS MIN_IO ALI_OSS HUAWEI_OSS QINIU_OSS
    delFile: false        # 调用删除接口是否删除文件,  设置为false只删除数据库记录
    local:   # 文件存储到服务器本机
      storage-path: /data/projects/uploadfile/file/   # 文件存储到服务器的绝对路径(需要启动项目前创建好,并且需要有读和写的权限!)
      endpoint: http://127.0.0.1/file/   # 外网文件访问前缀 (部署nginx后,配置为nginx的访问地址,并需要在nginx配置文件中静态代理storage-path)
      inner-uri-prefix: null  		# 内网的url访问前缀
    ali:    # 文件存储到阿里云OSS
      # 请填写自己的阿里云存储配置
      uriPrefix: "http://acuity-admin-cloud.oss-cn-beijing.aliyuncs.com/"
      bucket-name: "acuity-admin-cloud"
      endpoint: "oss-cn-beijing.aliyuncs.com"
      access-key-id: "填写你的id"
      access-key-secret: "填写你的秘钥"
    minIo:  # 文件存储到MINIO
      endpoint: "https://127.0.0.1:9000/"
      accessKey: "acuity"
      secretKey: "acuity"
      bucket: "dev"
    huawei:  # 文件存储到华为云OBS
      uriPrefix: "dev.obs.cn-southwest-2.myhuaweicloud.com"
      endpoint: "obs.cn-southwest-2.myhuaweicloud.com"
      accessKey: "1"
      secretKey: "2"
      location: "cn-southwest-2"
      bucket: "dev"
    qiNiu:    # 文件存储到七牛云OSS
      zone: "z0"
      accessKey: "1"
      secretKey: "2"
      bucket: "acuity_admin_cloud"
      
# acuity.file.storageType=FAST_DFS 时,需要配置
fdfs:
  soTimeout: 1500
  connectTimeout: 600
  thumb-image:
    width: 150
    height: 150
  tracker-list:
    - 192.168.1.2:22122
  pool:
    #从池中借出的对象的最大数目
    max-total: 153
    max-wait-millis: 102
    jmx-name-base: 1
    jmx-name-prefix: 1

修改配置服务器存储类型

存储类型支持LOCAL、FAST_DFS、MIN_IO、ALI_OSS、HUAWEI_OSS、QINIU_OSS,详见:FileStorageType.java。 存储类型支持热更新

yaml
acuity:
  file:
    storageType: MIN_IO
acuity:
  file:
    storageType: MIN_IO

程序启动时,会将所有存储类型的文件存储都初始化到内存,在执行上传接口时,根据上传接口或yaml中配置的存储类型参数,选择使用那个实现类。

java
public FileContext(Map<String, FileStrategy> map,
                       Map<String, FileChunkStrategy> chunkMap,
                       FileServerProperties fileServerProperties,
                       FileMapper fileMapper) {
    // 利用 spring 注入功能,在程序启动时,将6种类型的存储实现注入到map
    map.forEach(this.contextStrategyMap::put);
    //...
}
private FileStrategy getFileStrategy(FileStorageType storageType) {
  // 上传接口若没有传递storageType参数,则使用yml中配置的acuity.file.storageType参数。
  storageType = storageType == null ? fileServerProperties.getStorageType() : storageType;
  FileStrategy fileStrategy = contextStrategyMap.get(storageType.name());
  ArgumentAssert.notNull(fileStrategy, "请配置正确的文件存储类型");
  return fileStrategy;
}
public FileContext(Map<String, FileStrategy> map,
                       Map<String, FileChunkStrategy> chunkMap,
                       FileServerProperties fileServerProperties,
                       FileMapper fileMapper) {
    // 利用 spring 注入功能,在程序启动时,将6种类型的存储实现注入到map
    map.forEach(this.contextStrategyMap::put);
    //...
}
private FileStrategy getFileStrategy(FileStorageType storageType) {
  // 上传接口若没有传递storageType参数,则使用yml中配置的acuity.file.storageType参数。
  storageType = storageType == null ? fileServerProperties.getStorageType() : storageType;
  FileStrategy fileStrategy = contextStrategyMap.get(storageType.name());
  ArgumentAssert.notNull(fileStrategy, "请配置正确的文件存储类型");
  return fileStrategy;
}

控制基础平台 - 系统功能 - 附件管理 - 删除功能 是否真删除存储到服务器或OSS中的文件

yaml
acuity:
  file:
    delFile: false        # true:删除数据库记录的同时,调用oss接口删除文件 ;false: 只删除数据库记录,保留文件
acuity:
  file:
    delFile: false        # true:删除数据库记录的同时,调用oss接口删除文件 ;false: 只删除数据库记录,保留文件

参数介绍

swagger文档切换到 文件服务 - 附件 - 附件上传

  • bizType : 每个业务定义一个唯一的业务类型字符串,如用户头像=USER__AVATAR, 商品缩略图=product_thumbnail。

    推荐的命名规则(参考:AppendixType.java):模块之间用双下划线分割,库名、表名、字段名有多个单词的用单下划线分割,

    {库名}__{表名}__{字段名}
    {库名}__{表名}__{字段名}
  • bucket : 上传文件到指定的桶 , 不传此参数,就默认上传到 acuity.file.xxx.bucket

  • storageType : 存储类型 , 不传此参数,就默认上传到 acuity.file.storageType

  • 返回值介绍

json
{
// 业务类型
    "bizType": "每个业务定义一个唯一字符串,建议:库名_表名_字段名",
    // 文件id
    "id": "1418756607709282304",
// 文件的唯一路径
    "path": "0000/每个业务定义一个唯一字符串,建议:库名_表名_字段名/2021/07/24/30cfb050e5bb40868cc66ea66006a083.png",
    "fileType": {
      "code": "IMAGE",
      "desc": "图片"
    },
    "bucket": "dev",
    "uniqueFileName": "30cfb050e5bb40868cc66ea66006a083.png",
    "fileMd5": null,
    "originalFileName": "调用流程.png",
    "contentType": "image/png",
    "suffix": "png",
    "size": "276776",
}
{
// 业务类型
    "bizType": "每个业务定义一个唯一字符串,建议:库名_表名_字段名",
    // 文件id
    "id": "1418756607709282304",
// 文件的唯一路径
    "path": "0000/每个业务定义一个唯一字符串,建议:库名_表名_字段名/2021/07/24/30cfb050e5bb40868cc66ea66006a083.png",
    "fileType": {
      "code": "IMAGE",
      "desc": "图片"
    },
    "bucket": "dev",
    "uniqueFileName": "30cfb050e5bb40868cc66ea66006a083.png",
    "fileMd5": null,
    "originalFileName": "调用流程.png",
    "contentType": "image/png",
    "suffix": "png",
    "size": "276776",
}

热更新

虽然FileServerProperties类上加了@RefreshScope注解,但仅以下参数支持热更新,没在下面的参数不支持。

原因是MinIO、FastDFS、七牛云等在启动时就初始化好了oss client,在运行过程中改了参数,只会影响FileServerProperties实例中的参数,并不会影响已经初始化好了的 oss client 内部的参数,所以MinIO、FastDFS、七牛云等无法热更新。

yaml
acuity:
  file:
    storageType: MIN_IO   
    delFile: false   
    local: 
      storage-path: /data/projects/uploadfile/file/  
      endpoint: http://127.0.0.1/file/  
      inner-uri-prefix: null  	
    ali: 
      uriPrefix: "http://acuity-admin-cloud.oss-cn-beijing.aliyuncs.com/"
      bucket-name: "acuity-admin-cloud"
      endpoint: "oss-cn-beijing.aliyuncs.com"
      access-key-id: "填写你的id"
      access-key-secret: "填写你的秘钥"
    huawei:   
      uriPrefix: "dev.obs.cn-southwest-2.myhuaweicloud.com"
      endpoint: "obs.cn-southwest-2.myhuaweicloud.com"
      accessKey: "1"
      secretKey: "2"
      location: "cn-southwest-2"
      bucket: "dev"
acuity:
  file:
    storageType: MIN_IO   
    delFile: false   
    local: 
      storage-path: /data/projects/uploadfile/file/  
      endpoint: http://127.0.0.1/file/  
      inner-uri-prefix: null  	
    ali: 
      uriPrefix: "http://acuity-admin-cloud.oss-cn-beijing.aliyuncs.com/"
      bucket-name: "acuity-admin-cloud"
      endpoint: "oss-cn-beijing.aliyuncs.com"
      access-key-id: "填写你的id"
      access-key-secret: "填写你的秘钥"
    huawei:   
      uriPrefix: "dev.obs.cn-southwest-2.myhuaweicloud.com"
      endpoint: "obs.cn-southwest-2.myhuaweicloud.com"
      accessKey: "1"
      secretKey: "2"
      location: "cn-southwest-2"
      bucket: "dev"

当然,你也可以自行修改代码让MinIO、FastDFS、七牛云等实现热更新,以MinIO举例,方法如下:

  1. 注释FileAutoConfigure中的MinioClient
java
// FileAutoConfigure 注释以下代码
@Bean
public MinioClient minioClient(FileServerProperties properties) {
  return new MinioClient.Builder()
  .endpoint(properties.getMinIo().getEndpoint())
  .credentials(properties.getMinIo().getAccessKey(), properties.getMinIo().getSecretKey())
  .build();
}
// FileAutoConfigure 注释以下代码
@Bean
public MinioClient minioClient(FileServerProperties properties) {
  return new MinioClient.Builder()
  .endpoint(properties.getMinIo().getEndpoint())
  .credentials(properties.getMinIo().getAccessKey(), properties.getMinIo().getSecretKey())
  .build();
}
  1. 修改MinIoFileStrategyImpl代码
java
// MinIoFileStrategyImpl

// 1. 构造函数不在注入 MinioClient
// private final MinioClient minioClient;
public MinIoFileStrategyImpl(FileServerProperties fileProperties/*, MinioClient minioClient*/,
                              FileMapper fileMapper) {
  super(fileProperties, fileMapper);
  // this.minioClient = minioClient;
}

@Override
protected void uploadFile(File file, MultipartFile multipartFile, String bucket) throws Exception {
	// 划重点: 七牛云 和 FastDFS 也类似,在需要 oss client 的地方,取properties中的参数重新构建一个client,就能实现热更新。
  // 2. 在需要minioClient的地方,每次都重新构建
  MinioClient minioClient = new MinioClient.Builder()
                .endpoint(properties.getMinIo().getEndpoint())
                .credentials(properties.getMinIo().getAccessKey(), properties.getMinIo().getSecretKey())
                .build();

  boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
  // ...
}
// MinIoFileStrategyImpl

// 1. 构造函数不在注入 MinioClient
// private final MinioClient minioClient;
public MinIoFileStrategyImpl(FileServerProperties fileProperties/*, MinioClient minioClient*/,
                              FileMapper fileMapper) {
  super(fileProperties, fileMapper);
  // this.minioClient = minioClient;
}

@Override
protected void uploadFile(File file, MultipartFile multipartFile, String bucket) throws Exception {
	// 划重点: 七牛云 和 FastDFS 也类似,在需要 oss client 的地方,取properties中的参数重新构建一个client,就能实现热更新。
  // 2. 在需要minioClient的地方,每次都重新构建
  MinioClient minioClient = new MinioClient.Builder()
                .endpoint(properties.getMinIo().getEndpoint())
                .credentials(properties.getMinIo().getAccessKey(), properties.getMinIo().getSecretKey())
                .build();

  boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
  // ...
}

接口介绍

在本系统中,文件上传的场景有2种。

  1. 文件数据存储租户库(acuity_pro_base):明确知道上传请求和回显文件请求是归属于那个租户的。

    如:基础平台 - 消息中心 - 消息管理 - 发送消息,富文本编辑器中的上传接口,上传到租户库。

  2. 文件数据存储默认库(acuity_pro_defaults):无法确定请求是来自那个租户。

    如:开发运营系统 - 应用管理 - 应用维护,上传应用的logo,上传到默认库。

    如:开发运营系统 - 租户管理 - 租户维护,上传租户头像,上传到默认库。

    如:右上角 - 个人中心,上传用户头像,上传到默认库。

可以简单理解为:你的业务表存在于默认库,则上传、下载、回显附件也需要调用默认库的相关接口。你的业务表存在于租户库,则上传、下载、回显附件也需要调用租户库的相关接口。所以,上传、下载、回显接口有以下几个:

接口描述租户库默认库接口位置
上传文件/file/anyone/upload/file/anyone/uploadacuity-base-controller
根据文件id打包下载文件/file/anyone/download/file/anyone/downloadacuity-base-controller
根据文件id查询文件的临时访问路径/file/anyone/findUrlById/file/anyone/findUrlByIdacuity-base-controller
根据业务id 和 业务类型查询文件信息/anyone/appendix/listByBizId/anyone/appendix/listByBizIdacuity-file-sdk

表结构介绍

文件表有2张,这2张表的区别在于:

  1. com_file: 存储全量数据

  2. com_appendix: 存储当前某个业务的实时数据

如:新增应用时,上传了3次文件(点击了3次确认并上传),然后在点击确认按钮保存应用数据,com_file 表会存3条数据,com_appendix 存1条数据。

每次点击确认并上传,文件会立即上传到文件服务,所以若你上传了N次文件,但最后不保存应用数据,com_file表也会存在N条数据,第三方OSS中也还存在N个已经上传的文件,但 com_appendix 表中无数据。

所以,com_filecom_appendix的表结构基本一致,有如下异同点:

  1. com_appendix表比com_file多了1个字段:biz_id;少的其他字段都是无关紧要,若你想保持一致,加上去也行。

  2. com_appendix表的idbiz_idbiz_type 3个字段时必须的,其他字段都是为了冗余数据,方便业务数据查询和回显时才加上去的。

  3. 2张表的id是一致的,com_file表的数据一定多于或等于com_appendix表。

  4. 表中的url字段表示文件是公开bucket时的访问链接,若bucket是私有的,该字段的存储的链接无法将无法使用。

    私有bucket的文件访问连接需要在使用时临时生成,并且每次生成的链接都有有效期。

上传流程:

附件、图片等上传,一般都会跟业务表单进行关联(如:新增应用、编辑应用),为了使业务逻辑和文件存储逻辑尽可能的解耦,文件逻辑被独立封装成文件服务^1文件sdk^2

上传流程:

  1. 文件实时上传/file/anyTenant/upload/file/anyone/upload

    json
    {
      label: t('devOperation.application.defApplication.logo'),
      field: 'appendixIcon',
      component: 'CropperAvatar',
      componentProps: {
        uploadParams: { bizType: FileBizTypeEnum.DEF_APPLICATION_LOGO },
        circled: false,
        isDef: true,  # true:调用 /file/anyTenant/upload;false:调用/file/anyone/upload
      }, 
    },
    {
      label: t('devOperation.application.defApplication.logo'),
      field: 'appendixIcon',
      component: 'CropperAvatar',
      componentProps: {
        uploadParams: { bizType: FileBizTypeEnum.DEF_APPLICATION_LOGO },
        circled: false,
        isDef: true,  # true:调用 /file/anyTenant/upload;false:调用/file/anyone/upload
      }, 
    },

    文件上传后,将返回以下数据。

    若上传N次,com_file表会立即新增N条数据,此时不会存入com_appendix表。

    若一个表单有多个字段都需要上传附件,则需要为每个字段定义一个业务类型(bizType),bizType的规则建议为:{库}__{表}__

  2. 前端将文件实时上传接口调用返回的数据封装到业务接口的参数。

    将传成功后,CropperAvatar组件会自动将文件数据封装到appendixIcon字段。java的实体类需要使用对象来接收此参数。

    java
    public class DefApplicationSaveVO implements Serializable {
      @ApiModelProperty("图标")
      @Valid  // 表示需要校验此对象
      private AppendixSaveVO appendixIcon;
    }
    public class DefApplicationSaveVO implements Serializable {
      @ApiModelProperty("图标")
      @Valid  // 表示需要校验此对象
      private AppendixSaveVO appendixIcon;
    }
  3. 调用业务接口保存业务数据 + 附件数据

    java
    // defaults 库 def_appendix 表操作接口
    @Resource
    private DefAppendixService appendixService;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public DefApplication save(DefApplicationSaveVO saveVO) {
    		// ...
      	// 保存业务数据
        superManager.save(defApplication);
        // 保存附件数据到 defaults 库
        appendixService.save(defApplication.getId(), saveVO.getAppendixIcon());
        return defApplication;
    }
    // defaults 库 def_appendix 表操作接口
    @Resource
    private DefAppendixService appendixService;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public DefApplication save(DefApplicationSaveVO saveVO) {
    		// ...
      	// 保存业务数据
        superManager.save(defApplication);
        // 保存附件数据到 defaults 库
        appendixService.save(defApplication.getId(), saveVO.getAppendixIcon());
        return defApplication;
    }

    调用appendixService.save后,会在com_appendix表存入一条数据(com_file的N条数据仍然保留),defApplication.getId() 存入 biz_id 字段,FileBizTypeEnum.DEF_APPLICATION_LOGO 存入 biz_type字段。

文件回显流程:

  1. 根据业务id 和 业务类型查询文件信息

    typescript
    # Edit.vue
    const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
      // ...
    
      if (unref(type) !== ActionEnum.ADD) {
        const record = { ...data?.record };
        // 调用 acuity-file-sdk 模块的 /anyone/appendix/listByBizId 接口,根据业务id和业务类型查询附件信息
        const appendixIcons = await listByBizId({
                        prefix: ServicePrefixEnum.TENANT,
                        bizId: record.id,
                        isDef: true,
                        bizType: FileBizTypeEnum.DEF_APPLICATION_LOGO,
        });
        record.appendixIcon = appendixIcons?.[0];
        
        // 赋值到表单
        await setFieldsValue(record);
      }
    
      // ...
    });
    # Edit.vue
    const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
      // ...
    
      if (unref(type) !== ActionEnum.ADD) {
        const record = { ...data?.record };
        // 调用 acuity-file-sdk 模块的 /anyone/appendix/listByBizId 接口,根据业务id和业务类型查询附件信息
        const appendixIcons = await listByBizId({
                        prefix: ServicePrefixEnum.TENANT,
                        bizId: record.id,
                        isDef: true,
                        bizType: FileBizTypeEnum.DEF_APPLICATION_LOGO,
        });
        record.appendixIcon = appendixIcons?.[0];
        
        // 赋值到表单
        await setFieldsValue(record);
      }
    
      // ...
    });
  2. 根据文件id查询文件的临时访问路径

    大多数OSS的文件都分为公开bucket和私有bucket,私有bucket的访问连接需要在使用时实时获取,同一个文件每次获取的链接都不一样,且链接有失效时间。公开bucket的文件则可以使用固定链接访问。

    为了保证文件的访问流程一致,acuity系统约定无论是公开和私有bucket的文件,访问链接都需要每次访问时获取!

    typescript
    // Cropper和Upload组件内部都会 监听value值,给value赋值后,会调用/file/anyone/findUrlById 或 /file/anyone/findUrlById 方法获取文件的临时访问路径。
    watch(
      () => props.value,
      () => {
        realSrc.value = '';
        if (props.value && props.value.id) {
          loadSrc();
        }
      },
      { immediate: true },
    );
    
    function loadSrc() {
      if (!props.value.id) {
        return;
      }
      // 发起请求 获取临时访问路径
      const api = props.isDef ? asyncFindDefUrlById : asyncFindUrlById;
      api(props.value.id).then((res) => {
        if (res.code === 0) {
          realSrc.value = res.data as string;
        }
      });
    }
    // Cropper和Upload组件内部都会 监听value值,给value赋值后,会调用/file/anyone/findUrlById 或 /file/anyone/findUrlById 方法获取文件的临时访问路径。
    watch(
      () => props.value,
      () => {
        realSrc.value = '';
        if (props.value && props.value.id) {
          loadSrc();
        }
      },
      { immediate: true },
    );
    
    function loadSrc() {
      if (!props.value.id) {
        return;
      }
      // 发起请求 获取临时访问路径
      const api = props.isDef ? asyncFindDefUrlById : asyncFindUrlById;
      api(props.value.id).then((res) => {
        if (res.code === 0) {
          realSrc.value = res.data as string;
        }
      });
    }

下载:

  1. acuity.file.storageType = LOCAL,下载和预览时需要通过nginx才能访问 文件上传之后, 返回的文件url, 是无法直接在线访问的, 需要通过nginx进行代理才能访问。.
  • nginx 配置
nginx
server {
  listen       10000;
  # 这个路径要配置成 acuity.file.local.storage-path 最后一段路径.
  # 如: acuity.file.local.storage-path = /data/projects/uploadfile/file/ , 则需要配置为 location ^~ /file
  # 如: acuity.file.local.storage-path = /data/projects/uploadfile/abcd/ , 则需要配置为 location ^~ /abcd
  location ^~ /file {
      if ($request_uri ~* ^.*\/(.*)\.(apk|java|txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx|jpg|png)(\?fileName=([^&]+))$) {
          add_header Content-Disposition "attachment;filename=$arg_attname";
      }
      # 这个地址要配置成 acuity.file.local.storage-path 并去除最后一段路径 /file/ 或 /abcd/ 后的地址.
      root /data/projects/uploadfile;
      index index.html;
  }
}
server {
  listen       10000;
  # 这个路径要配置成 acuity.file.local.storage-path 最后一段路径.
  # 如: acuity.file.local.storage-path = /data/projects/uploadfile/file/ , 则需要配置为 location ^~ /file
  # 如: acuity.file.local.storage-path = /data/projects/uploadfile/abcd/ , 则需要配置为 location ^~ /abcd
  location ^~ /file {
      if ($request_uri ~* ^.*\/(.*)\.(apk|java|txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx|jpg|png)(\?fileName=([^&]+))$) {
          add_header Content-Disposition "attachment;filename=$arg_attname";
      }
      # 这个地址要配置成 acuity.file.local.storage-path 并去除最后一段路径 /file/ 或 /abcd/ 后的地址.
      root /data/projects/uploadfile;
      index index.html;
  }
}
  • yml 配置
yaml
acuity:
 file:
   storageType: LOCAL 
   local:
     storage-path: /data/projects/uploadfile/file/  
     uriPrefix: http://127.0.0.1:10000/file/   # 配置成nginx的ip + 端口
    
# 或者    
acuity:
 file:
   storageType: LOCAL 
   local:
     storage-path: /data/projects/uploadfile/abcd/  
     uriPrefix: http://127.0.0.1:10000/abcd/   # 配置成nginx的ip + 端口
acuity:
 file:
   storageType: LOCAL 
   local:
     storage-path: /data/projects/uploadfile/file/  
     uriPrefix: http://127.0.0.1:10000/file/   # 配置成nginx的ip + 端口
    
# 或者    
acuity:
 file:
   storageType: LOCAL 
   local:
     storage-path: /data/projects/uploadfile/abcd/  
     uriPrefix: http://127.0.0.1:10000/abcd/   # 配置成nginx的ip + 端口

按上述配置后, 启动nginx 就可以直接访问文件: http://192.168.2.178:10000/file/0000/2020/12/92b19723-3b20-4e73-a089-de31cf02883e.png

预览

保证自己上传的文件能通过url直接访问之后, 安装 kkFileView 实现附件预览。

欢迎使用天源云Saas快速开发系统