---
source: "https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/e1dfa75b-a524-453d-b15c-c64c0119db3d"
publicId: "e1dfa75b-a524-453d-b15c-c64c0119db3d"
---

# 后端存储底层二开

### **快速上手**

***1.开发流程介绍***

> - 集成模式的相关介绍请看[二开手册简介-相关术语介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/6ebe70ee-ae5b-435b-904b-41c0a40ad04e)
> - 前端存储配置页面二开和后端底层存储二开放在同一个二开项目中，生成一个二开jar包

1. 使用集成模式二开
2. 创建本地ecode二开项目weaver-storage-secondev
3. 继承抽象类新增实现类
4. 实现抽象类中的各个方法
5. ecode二开项目打包成二开jar包
6. 集成到所有e10服务中
7. 二开结束

### **二开准备**

1. 文件服务存储二开是基于 ecode [IDEA-本地项目二开 ](https://weapp.eteams.cn/ecode/playground/doc/share/view/974701716410515473#IDEA-%E6%9C%AC%E5%9C%B0%E9%A1%B9%E7%9B%AE%E4%BA%8C%E5%BC%80%E8%AF%B4%E6%98%8E "IDEA-本地项目二开)，请务必熟练掌握相关功能操作。
2. 创建code本地二开项目：weaver-file-storage-secondev 二开项目

### **实现方法说明**

#### ***1. 继承抽象类构建实现类（必要）***

> 抽象基础类：[com.weaver.teams.storage](http://com.weaver.teams.storage).secondary.BaseSecondaryStoStrategy

```
// 示例代码
public class DemoStoragetStrategy extends BaseSecondaryStoStrategy{}
```

#### ***2. 实现定义自定义存储的标识（必要）***

> FileStorage说明：[存储标识类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/8a2cc328-491e-4575-99ec-215b0232c285)

```
FileStorage getStorageType() {}
```

```
// 示例代码
public class DemoStoragetStrategy extends BaseSecondaryStoStrategy {
    public final static String CUSTOM_STORAGE_KEY = "xxxxxxxxx";
    public final static FileStorage CUSTOM_STORE = FileStorage.of("b60***e7", CUSTOM_STORAGE_KEY);
    @Override
    public FileStorage getStorageType() {
        return CUSTOM_STORE;
    }
}
```

#### ***3. 实现自定义存储初始化默认配置（必要）***

> - 此方法用于系统启动初始化存储，会根据此方法返回的值初始化一个全局默认存储实例
> - 建议此处初始化的配置参数放到nacos配置中（配置文件：[weaver-storage.properties](http://weaver-storage.properties)），代码中用@value获取
> - StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
StorageConfigInfo getDefaultConfig() {}
```

```
 出参处理提示：
1. StorageConfigInfo中的InstanceType一定要设置返回
2. 其他属性按需初始化
3. 前端二开的存储配置字段以extMap.开头的，需要放入StorageConfigInfo中的extMap,eg. 二开字段extMap.appName,则返回extmap.put("appName","这是默认应用名");
```

```
// 示例代码
public class DemoStoragetStrategy extends BaseSecondaryStoStrategy {

  public final static String CUSTOM_STORAGE_KEY = "xxxxxxxxx";
    public final static FileStorage CUSTOM_STORE = FileStorage.of("b60***e7", CUSTOM_STORAGE_KEY);

    @Value("${local.appKey:eteams}")
    private String appKey;
    @Value("${local.rootPath:/nfs/data}")
    private String rootPath;
    @Value("${local.static.i18n:}")
    private String staticI18N;
    @Value("${local.download.host:}")
    private String downloadHost;

    @Override
    public StorageConfigInfo getDefaultConfig() {
        StorageConfigInfo storageConfigInfo = new StorageConfigInfo();
        // instanceType字段一定要这么设置返回！！
        storageConfigInfo.setInstanceType(CUSTOM_STORE.getInstanceType());
        storageConfigInfo.setInstancePath(rootPath);
        storageConfigInfo.setOriInstancePath(rootPath);
        storageConfigInfo.setDownloadUrl(downloadHost);
        storageConfigInfo.setStaticI18N(staticI18N);
        return storageConfigInfo;
    }
}
```

#### ***4. 实现上传单个文件（必要）***

> - 这是上传单个文件的方法，后面还有分片上传的方法要实现
> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
String putFile(StorageParam storageParam){};
```

```
 入参处理提示：
 1.  storageParam.getResource(); 能通过resource.getInputStream()获取到待上传文件的文件流
 2.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 3.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 4.  storageParam.getStorageConfigInfo().getStorageAbsolutePath()；是文件将要存放的路径，例如："/nfs/data/tenantKey/xxxx/xxxx"
 5.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 6.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+ storageParam.getUrl()；可以拼出文件将要保存到的完整路径
 7.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 8.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用
 9.  storageParam.getUrl()为空时，需要自行定义一个，一般是uuid，最后作为结果返回
 10.  storageParam.getUrl()不为空，一般传入的是uuid，要用此保存文件，最后作为结果返回

 出参处理提示：
 1. 被存储文件的唯一标识，对应的就是其他方法（下载、复制等）入参中的storageParam.url
```

```
// 示例代码
public String putFile(StorageParam storageParam) throws IOException {
    // extAttributes中一般存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
    Map<String, Object> extAttributes = storageParam.getExtAttributes();
    StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
    // 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值
    Map<String,String> extMap = configInfo.getExtMap();
    Asserts.notNull(configInfo, "putFile storageConfigInfo");
    // Resource有多种实现，一般直接resource.getInputStream()即可获得文件流
    Resource resource = storageParam.getResource();
    Asserts.notNull(resource, "putFile resource");
    // 文件存储标识url（一般是uuid）
    String url = storageParam.getUrl();
    // url传空时，需要自行定义一个，最后作为结果返回
    // url不为空时，要使用该url去保存，不能变动
    if (StringUtils.isBlank(url)) {
        url = this.localStorageClient.getLocalUUID();
    }
    // storageAbsolutePath 可以获取文件将要存储到的绝对路径
    //storageAbsolutePath + "/"+url,就是一个文件将要在存储服务器中存放的完整路径
    String storageAbsolutePath = storageConfigInfo.getStorageAbsolutePath();
    // 上传到自定义存储中
    this.localStorageClient.saveFileToPath(storageAbsolutePath, resource, url);
    return url;
}
```

#### ***5. 实现下载文件获取文件流（必要）***

> - 下载文件获取文件流
> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
OSSFilePo getFilePo(StorageParam storageParam){};
```

```
 入参处理提示：
 1.  storageParam.getResource(); 能通过resource.getInputStream()获取到待上传文件的文件流
 2.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 3.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 4.  storageParam.getStorageConfigInfo().getStorageAbsolutePath()；是文件将要存放的路径，例如："/nfs/data/tenantKey/xxxx/xxxx"
 5.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 6.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+ storageParam.getUrl()；可以拼出文件将要保存到的完整路径
 7.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 8.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用
 9. storageParam.getStart()和storageParam.getEnd()都不为空时，表示下载指定范围的文件流片段
 9-1. 文件流片段返回对象的处理，都可以使用com.qcloud.cos.internal.InputSubstream(InputStream in, long offset, long length, boolean closeSourceStream)；

出参处理提示：
1. ossFilePo.setInputStreamPart(true);返回的是分段文件流时，一定要将此参数标记为true
2. ossFilePo.setStorageClient(this.localStorageClient); 一般返回的是已经和当前存储服务器建立连接的客户端对象，用以后续文件操作中客户端的复用，以提升性能。
```

```
// 示例代码
public OSSFilePo getFilePo(StorageParam storageParam) throws Exception {
    StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
    Asserts.notNull(configInfo, "storageConfigInfo is null");
    String url = storageParam.getUrl();
    StorageUtils.validate(StringUtils.isBlank(url), "getFilePo url is blank");
    // start和end表示字节索引，指定下载从start到end位置的文件流
    Long start = storageParam.getStart();
    Long end = storageParam.getEnd();

    // storageAbsolutePath 可以获取文件将要存储到的绝对路径
    //storageAbsolutePath + "/"+url,就是一个文件将要在存储服务器中存放的完整路径
    String storageAbsolutePath = storageConfigInfo.getStorageAbsolutePath();
    OSSFilePo ossFilePo = new OSSFilePo();
    InputStream inputStream = this.localStorageClient.downloadFile(storageAbsolutePath, url);
    if (start != null && end != null) {
        int startInt = start.intValue();
        int endInt = end.intValue();
        int currentFileSize = end - start + 1;
        inputStream =  new InputSubstream(inputStream, start, currentFileSize,true);
        ossFilePo.setInputStreamPart(true);
    }

    ossFilePo.setInputStream(inputStream);
    ossFilePo.setStorageClient(this.localStorageClient);
    return ossFilePo;
}
```

#### ***6. 实现删除文件（必要）***

> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
int deleteFile(StorageParam storageParam) {};
```

```
 入参处理提示：
 1.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 2.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 3.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+storageParam.getUrl()；可以拼出待删除文件的完整路径
 4.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 5.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用

出参处理提示：
1. 0 表示删除成功 1表示删除失败
```

```
// 示例代码
public int deleteFile(StorageParam storageParam) throws Exception {
    StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
    Asserts.notNull(configInfo, "deleteFile storageConfigInfo");
    Asserts.notEmpty(storageParam.getUrl(), "deleteFile url");
    String fileKey = StorageUtils.getLocalAbsolutePath(configInfo);
    this.deleteFile(storageParam.getUrl(), fileKey);
    return 0;
}
```

#### ***7. 实现初始化分片上传（必要）***

> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
Map<String, String> initiateMultipartUpload(StorageParam storageParam){};
```

```
 入参处理提示：
 1.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 2.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 3.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+storageParam.getUrl()；可以拼出待删除文件的完整路径
 4.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 5.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用

出参处理提示：
1. fileId,等同于storageParam.url,文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
1-1. fileId要自行定义一个，一般是uuid,最终最为结果返回
2. uploadId，分片上传任务ID，当前文件分片上传的任务标识，此文件的每一个分片上传时，都关联到此任务ID
```

```
// 示例代码
    @Override
    public Map<String, String> initiateMultipartUpload(StorageParam storageParam) {
        StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
        Asserts.notNull(configInfo, "storageConfig is null");
        String fileId = localStorageClient.getLocalUUID();
        String fileKey = StorageUtils.getLocalAbsolutePath(configInfo);
        String uploadId = localStorageClient.initMultipartUpload(fileKey, fileId);
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("fileId", fileId);
        resultMap.put("uploadId", uploadId);
        return resultMap;
    }
```

#### ***8. 实现上传分片（必要）***

> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
Map<String, String> uploadPart(StorageParam storageParam){};
```

```
 入参处理提示：
 1.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 2.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 3.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+storageParam.getUrl()；可以拼出待删除文件的完整路径
 4.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 5.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用
 6.  storageParam.getUploadId(); Map<String, String> initiateMultipartUpload(StorageParam storageParam) 初始化分片上传接口返回值中的任务ID
 7.  storageParam.getPartNumber(); 当前上传的是第几片分片
 8.  storageParam.getPartSize(); 当前上传的分片大小
 9.  storageParam.getResource(); 能通过resource.getInputStream()获取到当前待上传分片的文件流

出参处理提示：
1. UploadPartResult, json字符串
1-1. json字符串中要包含一下字段的键值对，partSize（必要）、partNumber（必要）、Etag（可选）
1-2. partSize: 当前分片大小、partNumber： 当前是第几分片、Etag 当前分片的md5值
1-3. 计算md5值可以使用：com.weaver.teams.storage.utils.StorageUtils#calculateMD5(byte[])
1-4. 计算md5值回消耗掉当前文件流，导致后续的文件操作，无文件流可用，所以一般要复制一份文件流专门用于计算md5值
```

```
// 示例代码
    @Override
    public Map<String, String> uploadPart(StorageParam storageParam) throws IOException {
        StorageConfigInfo configInfo = Optional.ofNullable(storageParam.getStorageConfigInfo()).orElse(new StorageConfigInfo());
        Asserts.notNull(configInfo, "uploadPart storageConfigInfo is null");
        String uploadId = storageParam.getUploadId();
        Asserts.notEmpty(uploadId, "uploadPart uploadId is null");
        String url = storageParam.getUrl();
        Asserts.notEmpty(url, "uploadPart url is null");
        Integer parNumber = storageParam.getPartNumber();
        Asserts.notNull(parNumber, "uploadPart partNumber is null");
        Long partSize = storageParam.getPartSize();
        Asserts.notNull(partSize, "uploadPart partSize is null");
        Resource resource = storageParam.getResource();
        Asserts.notNull(resource, "uploadPart resource is null");
        String storageAbsolutePath = StorageUtils.getLocalAbsolutePath(configInfo);
        String instancePath = configInfo.getInstancePath();
        Map<String, Object> localPartResultMap = localStorageClient
                .uploadPart(storageAbsolutePath, url, uploadId, parNumber, partSize, resource.getInputStream(), instancePath);
        JSONObject json = JSONObject.fromObject(localPartResultMap);
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("UploadPartResult", json.toString());
        return resultMap;
    }
}
```

#### ***9. 实现合并分片（必要）***

> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
Map<String, String> completeMultipartUpload(StorageParam storageParam){};
```

```
 入参处理提示：
 1.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 2.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 3.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+storageParam.getUrl()；可以拼出待删除文件的完整路径
 4.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 5.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用
 6.  storageParam.getUploadId(); Map<String, String> initiateMultipartUpload(StorageParam storageParam) 初始化分片上传接口返回值中的任务ID

出参处理提示：
1. CompleteMultipartUploadResult, json字符串
1-1. json字符串中要包含一下字段的键值对，uploadId（必要）、fileId（必要）、parts（必要）
1-2. uploadId: 当前文件分片上传的任务ID
1-3. fileId：文件唯一标识，等同于StorageParam.url, 一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
1-4. parts：成功上传的分片数，可以复用【[11]查询已上传分片信息接口】获取到，第十一个要实现的接口
```

```
// 示例代码
    @Override
    public Map<String, String> completeMultipartUpload(StorageParam storageParam) {
        StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
        Asserts.notNull(configInfo, "completeMultipartUpload storageConfigInfo is null");
        String url = storageParam.getUrl();
        Asserts.notEmpty(url, "completeMultipartUpload url is null");
        String uploadId = storageParam.getUploadId();
        Asserts.notEmpty(uploadId, "uploadId is null");
        String fileKey = StorageUtils.getLocalAbsolutePath(configInfo);
        String rootPath = configInfo.getInstancePath();
        Map<String, Object> mergePartMap = localStorageClient.mergePart(fileKey, url, uploadId, rootPath);
        JSONObject json = JSONObject.fromObject(mergePartMap);
        Map<String, String> map = new HashMap<>(16);
        map.put("CompleteMultipartUploadResult", json.toString());
        return map;
    }
```

#### ***10. 实现终止合并分片（必要）***

> - StorageParam统一说明：存储底层统一入参封装类介绍
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
 void abortMultipartUpload(StorageParam storageParam){};
```

```
 入参处理提示：
 1.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 2.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 3.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+storageParam.getUrl()；可以拼出待删除文件的完整路径
 4.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 5.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用
 6.  storageParam.getUploadId(); Map<String, String> initiateMultipartUpload(StorageParam storageParam) 初始化分片上传接口返回值中的任务ID
```

```
// 示例代码
    @Override
    public void abortMultipartUpload(StorageParam storageParam) {
        StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
        Asserts.notNull(configInfo, "config is null");
        String fileKey = StorageUtils.getLocalAbsolutePath(configInfo);
        String rootPath = configInfo.getInstancePath();
        localStorageClient.abortMultiPartUpload(fileKey, storageParam.getUrl(), storageParam.getUploadId(), rootPath);
    }
}
```

#### ***11. 实现查询已上传分片信息（必要）***

> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
String listParts(StorageParam storageParam){};
```

```
 入参处理提示：
 1.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 2.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 3.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+storageParam.getUrl()；可以拼出待删除文件的完整路径
 4.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 5.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用
 6.  storageParam.getUploadId(); Map<String, String> initiateMultipartUpload(StorageParam storageParam) 初始化分片上传接口返回值中的任务ID

出参处理提示：
1.  json字符串；来自Map<String, Map<String, String>>类型的序列化
1-1. key: 分片号 value: Map<String, String>当前分片的具体信息，eg: key : 1, value : {partSize:4096,md5:xxxxxxxxxx}
1-1-1. value：Map<String, String>， 需要包含有一下partSize（必要）、md5（可选）
1-1-2. partSize: 分片大小，md5： 分片md5值
```

```
// 示例代码
    @Override
    public String listParts(StorageParam storageParam) {
        StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
        Asserts.notNull(configInfo, "storageConfig is null");
        Asserts.notEmpty(storageParam.getUrl(), "url is null");
        Asserts.notEmpty(storageParam.getUploadId(), "uploadId is null");
        String fileKey = StorageUtils.getLocalAbsolutePath(configInfo);
        String instancePath = configInfo.getInstancePath();
        Map<String, Map<String, String>> partsMap = localStorageClient.listParts(fileKey, storageParam.getUrl(), storageParam.getUploadId(), instancePath);
        JSONObject json = JSONObject.fromObject(partsMap);
        return json.toString();
    }
```

#### ***12. 实现生成存储服务直接下载地址（必要）***

> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
String generatePreSignedUrl(StorageParam storageParam) {};
```

```
 入参处理提示：
 1.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 2.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 3.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+storageParam.getUrl()；可以拼出待下载文件的完整路径
 4.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 5.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用
 6.  storageParam.getResponseHeaderSettings(); 响应头会用到的属性，按需使用，根据情况不同，不同的属性才会有值，一般可以直接忽略
 6-1. 包含属性有：contentType、contentLanguage、expires、cacheControl、contentDisposition、contentEncoding、fileName、tenantKey、downLoadUrl、extDownLoadUrl
 7. storageParam.getDownload(); 当前下载链接是否只用于下载，否的话，标识下载链接用于预览、下载等所有场景

出参处理提示：
1.  当前文件的存储服务器直接下载地址
```

```
// 示例代码
    @Override
    public String generatePreSignedUrl(StorageParam storageParam) {
        StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
        Asserts.notNull(configInfo, "storageConfig");
        String url = storageParam.getUrl();
        Asserts.notEmpty(url, "generatePreSignedUrl url");
        Date expiration = Optional.ofNullable(storageParam.getExpiration()).orElse(new Date(new Date().getTime() + 3600 * 1000));
        ResponseHeaderSettings responseHeaderSettings = storageParam.getResponseHeaderSettings();
        Asserts.notNull(responseHeaderSettings, "responseHeaderSettings is null");
        responseHeaderSettings.setExpires(String.valueOf(expiration));
        boolean isDownload = Optional.ofNullable(storageParam.getDownload()).orElse(false);
        Asserts.notEmpty(responseHeaderSettings.getTenantKey(), "generatePreSignedUrl tenantKey is null");
        return localStorageClient.generatePresignedUrl(responseHeaderSettings.getTenantKey(), url, isDownload, responseHeaderSettings);
    }
```

#### ***13. 实现复制文件（必要）***

> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
String copyFile(StorageParam storageParam) {};
```

```
 入参处理提示：
 1.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 2.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 3.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+storageParam.getUrl()；可以拼出待复制文件的完整路径
 4.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 5.  storageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用
 6.  storageParam.getDestStorageAbsolutePath(); 表示要复制到的目的地路径
 6-1. 如果为空，保持和storageParam.getStorageConfigInfo().getStorageAbsolutePath() 一致，表示同路径下的复制

出参处理提示：
1.  复制后新文件的url, uuid字符串，与原文件url要不同
```

```
// 示例代码
    @Override
    public String copyFile(StorageParam storageParam) {
        StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
        Asserts.notNull(configInfo, "copyFile storageConfig is null");
        String url = storageParam.getUrl();
        Asserts.notEmpty(url, "copyFile origin url is blank");
        String storageAbsolutePath = configInfo.getStorageAbsolutePath();
        Asserts.notEmpty(storageAbsolutePath, "copyFile storageAbsolutePath is blank");
        String destStorageAbsolutePath = storageParam.getDestStorageAbsolutePath();
        if (StringUtils.isBlank(destStorageAbsolutePath)) {
            destStorageAbsolutePath = storageAbsolutePath;
        }
        return localStorageClient.copyFile(storageAbsolutePath, url, destStorageAbsolutePath);
    }
```

#### ***14. 实现校验物理文件是否存在（必要）***

> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
boolean metadataExist(StorageParam storageParam) {};
```

```
 入参处理提示：
 1.  storageParam.getUrl(); 是文件唯一标识，一般上传到存储服务器时用作文件名，保证存储服务器中此物理文件的唯一不重复
 2.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 3.  storageParam.getStorageConfigInfo().getStorageAbsolutePath() +File.separator+storageParam.getUrl()；可以拼出待复制文件的完整路径
 4.  storageParam.getExtAttributes();存放了一些扩展参数，或者是其他地方二开中自定义的参数，按需使用
 5.  orageParam.getStorageConfigInfo().getExtMap(); 存储配置封装类扩展参数，用于传递存储配置二开中自定义的配置项和值,按需使用

出参处理提示：
1.  布尔值，表示物理文件是否存在
```

```
// 示例代码
    @Override
    public boolean metadataExist(StorageParam storageParam) {
        StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
        Asserts.notNull(configInfo, "config is null");
        Asserts.notNull(storageParam.getUrl(), "url is null");
        String fileKey = StorageUtils.getLocalAbsolutePath(configInfo);
        String filePathStr = localStorageClient.getFilePath(fileKey, storageParam.getUrl());
        Path filePath = Paths.get(filePathStr);
        return Files.exists(filePath, LinkOption.NOFOLLOW_LINKS);
    }
```

#### ***15. 实现校验两个物理文件是否同源（必要）***

> - 判断两个文件在存储服务器中是否属于同一个区域，例如：腾讯云存储中，两个文件必须同分区同一个桶才算同源
> - 此方法用于秒传逻辑
> - StorageParam统一说明：[存储底层统一入参封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/821dba42-8362-4d02-8916-fbbde713d27d)
> - StorageParam中的StorageConfigInfo说明请看：[存储相关配置统一封装类介绍](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/40ee76f1-df16-47fd-bf02-2dbf4eaec3ac)

```
boolean isSameOriginStorage(StorageParam storageParam) {}
```

```
 入参处理提示：
 1.  storageParam.getStorageConfigInfo(); 存储相关配置统一封装类
 2.  storageParam.getStorageConfigInfo(); 文件A存储采用存的储配置信息
 3.  storageParam.getDestStorageConfigInfo(); 文件B存储采用存的储配置信息

出参处理提示：
1.  布尔值，表示两个文件是否同源
```

```
// 示例代码
    @Override
public boolean isSameOriginStorage(StorageParam storageParam) {
    StorageConfigInfo configInfo = storageParam.getStorageConfigInfo();
    StorageConfigInfo destConfigInfo = storageParam.getDestStorageConfigInfo();
    if (configInfo != null && destConfigInfo != null) {
        String region = (String)Optional.ofNullable(configInfo.getRegion()).orElse("");
        String destRegion = (String)Optional.ofNullable(destConfigInfo.getRegion()).orElse("");
        String bucket = (String)Optional.ofNullable(configInfo.getBucket()).orElse("");
        String destBucket = (String)Optional.ofNullable(destConfigInfo.getBucket()).orElse("");
        return (new EqualsBuilder()).append(region, destRegion).append(bucket, destBucket).isEquals();
    } else {
        return false;
    }
}
```
