---
source: "https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/9304010e-b3bf-4199-86b3-639d2a7f8694"
publicId: "9304010e-b3bf-4199-86b3-639d2a7f8694"
---

# 外部数据迁移到E10 开发方案

# 简要说明：

1、此文档是用于将外部系统/第三方的附件迁移到E10系统中的方案，迁移方案中涉及到一些迁移开发，请仔细阅读文档的每一个细节！

2、迁移前请项目人员提前确认E10文件存储使用什么存储类型【[如何确认E10系统中使用的是什么存储](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/24a71bee-02f7-4fe8-934a-c06eb6b43eac)】

**注意事项**

- 请确认E10系统的存储已经配置，且已经处于可用状态。如何配置存储，请参考【[E10文件存储设置](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/a84e45f1-834a-45ad-b96f-3e9d92d1c409)】

# **1、方案一**

## **1.1、适用场景：**

** （1）、文件量很大、TB级别的文件大小。**

**（2）、第三方系统中的文件，有完整的迁移方案，或者迁移工具，能直接将文件迁移到 E10 的存储中**

## **1.2、迁移思路**

** （1）先将物理文件整体迁移到 E10 的存储中**

**场景举例说明：**

**场景1: **第三方系统的文件存在某文件服务器下，数量有10TB，客户E10的系统使用华为云，客户使用华为云的迁移平台直接将文件服务器上的文件迁移到了华为云上。

**场景2: **第三方系统的文件存在某文件服务器下，数量有10TB，客户E10的系统使用单独的文件服务器存储文件，客户使用 FTP 工具，先将物理文件迁移到了E10 的文件服务器上。

 注意：此步骤需要客户或者项目去完成，总部不提供迁移工具和人力投入

**（2）物理文件迁移完成后，再生成E10的文件逻辑数据，生成逻辑数据的步骤需要二开处理**

## **1.3、迁移步骤**

### **1.3.1、迁移物理文件**

**第一步：已经确认了E10使用的存储是否正常和存储路径**

假设你已经设置了E10的存储是本地存储NAS，并在此处已经设置了存储位置：

文件存储根路径为：**/nfs/data/test**，并且已经验证好上传下载的功能。

如果设置是对象存储，则确认了文件需要放在哪个桶下，如文件要放在阿里云的 test 桶下

注意：没验证存储是否生效、不要往下进行

**第二步：确认E10系统的文件存储路径规则**

在E10 系统中，请按照我们的规则，所有外部系统的文件，迁移到E10的存储中时，必须按照路径：

**/文件存储根路径/租户代码/年/月/日/UUID **

的存放方式进行迁移

如：D盘下的AAA目录有个ccc.docx文件，先用程序将文件名调整成UUID，并记录源文件名和UUID关系，当迁移到E10的本地存储，路径会是：/nfs/data/test/tj02iu5kla/2024/01/09/09c110e5-7d8e-41f5-b989-7b3c484e4d42。而/nfs/data/test 是你在上面截图中存储实例中已经定义好的根路径。如果是对象存储则是在test 桶下，路径为：/tj02iu5kla/2024/01/09/09c110e5-7d8e-41f5-b989-7b3c484e4d42  
![](https://showdoc.alphaxx.cn:19993/server/index.php?s=/api/attachment/visitFile&sign=e268019b85840736299bd1caf244f169)

自行使用程序（包括但不限于Java）将你整理好的文件，名字替换成UUID，String url = UUID.randomUUID().toString()-Java写法，我们强烈不建议迁移后文件名称原封不动落在E10文件存储的磁盘上，会加大泄露的风险。

若用Java程序，自行写代码读取文件名，并将文件名称替换成UUID，比如你整理好的文件统一在D盘下的AAA目录下，某个文件名称叫做ccc.docx，替换后假如是：13efc651-f092-4326-91e5-fd82702fd2d2（不要带后缀！）

请自行将所有的文件原名替换完成后，自行存好原文件名 和 UUID的映射关系，可以是excel，也可以自己搞个表记录，否则后面步骤中插入逻辑数据，你无法获取获取到真实的文件名称更新到表数据中！

**注意：若存在相同的 UUID，可能会导致相同文件被覆盖，或者业务功能异常，正常情况下的UUID生成是不会重复的。**

** 第三步：按照路径规则进行文件迁移，并迁移完成**

这里只是展示迁移至本地存储的情况，若E10接的是云存储，思路一样，也是在本地维护好这些文件，你甚至可以将文件按照上面的格式先完整维护好在本地，比如D:/AAA/1.txt，维护成 D:/nfs/data/test/租户代码/年/月/日/UUID，然后你可以完整copy到NAS上，路径保持不变。如在Linux，可以维护成/nfs/data/test/租户代码/年/月/日/UUID，这里一定要确保，你的路径前缀，要和存储根路径配置一样，并且已挂载！

### **1.3.2、附件逻辑数据生成**

物理文件迁移完成后，会拿到物理文件完整件存储路径 【/文件存储根路径/租户代码/年/月/日/UUID】的集合，读取文件完整的储路径后，进行数据的生成操作

根据【/文件存储根路径/租户代码/年/月/日/UUID】生成附件逻辑数据具体代码示例如下

```java
方法接口：

com.weaver.eteams.file.client.remote.UploadClientService

使用方式：

@RpcReference
private UploadClientService uploadClientService；
```

```

/**
 * 存储物理文件存储信息
 * @param storeResults 物理文件存储信息参数
 */
 default void saveStorageInfo(StoreResults storeResults){};

/**
 * 上传 rpc 接口，只做保存数据的逻辑；注意：此接口使用，请联系文件服务开发人员；使用前提：物理文件已迁移完成或者已经存入到存储中
 * @param simpleEmployee 用户信息
 * @param contentType 文件类型参数
 * @param remoteUploadParam 文件上传参数
 * @return FileObj 返回的附件信息对象
 */
 FileObj remoteUploadSaveData(SimpleEmployee simpleEmployee, String contentType,RemoteUploadParam remoteUploadParam)
```

#### ** 参数说明**

- simpleEmployee 用户信息对象，建议使用 com.weaver.common.hrm.util.HrmCommonUtil#getSimpleEmployee 传入人员信息id获取
- contentType，文件请求头，用户可以根据自定义的文件类型获取，或者通过工具类获取。contentType = new MimetypesFileTypeMap().getContentType(name); name 为文件名。
- **remoteUploadParam 参数说明**

| **参数名** | **必选** | **类型** | **说明** |
| --- | --- | --- | --- |
| name | 是 | String | 文件名 |
| lastModified | 是 | String | 文件最后修改时间，毫秒时间戳 如：lastModified = String.valueOf(System.currentTimeMillis()) |
| module | 是 | string | 模块名，需要和 EntityType 中保持一致 |
| size | 是 | Long | 文件大小 |
| url | 是 | String | 物理文件的关联信息，参数获取，请见下述说明 |
| StoreResults | 是 | Object | 存储信息对象，参数获取，请见下述说明 |
| refId | 否 | Long | 关联的业务id |
| commentFlag | 否 | Boolean | 标识是不是评论组件，用于获取契约锁的按钮信息，默认false |
| folderId | 否 | Long | 目录id |
| folderType | 否 | String | 目录类型 |
| source | 否 | String | 操作来源（用于标识，文件是从哪里操作上传，便于特殊业务逻辑处理）目前有3个取值， 1. comment ：来源于评论的附件；2. form 来源表单自定义附件字段；3.relation 来源关联附件字段 4.richText 来源富文本 5. fileAuth 主要用于记录鉴权参数 |
| createDoc | 否 | boolean | 是否创建文档 |
| tempFileFlag | 否 | boolean | 是否是临时文件存储 |
| tempFileTime | 否 | boolean | 临时文件的保存时间 时间单位 小时 |
| commonText | 否 | String | 附件自定义关联信息 |
| fieldId | 否 | Long | 附件在表单上的区域字段id |

- **StoreResults 参数说明**

| **参数名** | **必选** | **类型** | **说明** |
| --- | --- | --- | --- |
| currentTenantKey | 是 | String | 租户名称 |
| fileUrl | 是 | String | 物理文件的关联信息，参数获取，请见下述说明) |
| filePath | 是 | string | 物理文件存放路径 |
| md5Change | 是 | boolean | 标记MD5值是否改变，默认false |
| fileName | 是 | String | 文件名称 |
| fileRealSize | 是 | Long | 文件大小 |
| ruleVersionId | 是 | Long | 存储规则id |
| storageType | 是 | String | 存储类型 |

#### **调用示例：**

1. 物理文件已经迁移或物理文件已经上传到对应存储

例如：上传完成后的物理路径为

/oabucket/tj02iu5kla/2024/01/09/09c110e5-7d8e-41f5-b989-7b3c484e4d42

```java
@RpcReference
private UploadClientService uploadClientService;

@Autowired
private HrmCommonUtil hrmCommonUtil;

@RpcReference
private FileStoRuleMatchClient fileStoRuleMatchClient;

@GetMapping("/test2")
public void test(){

    //获取用户信息
    Long employeeId = 34434345653598L;
    SimpleEmployee simpleEmployee = hrmCommonUtil.getSimpleEmployee(employeeId);
    //获取文件类型
    String filename = "test.docx";
    String contentType = new MimetypesFileTypeMap().getContentType(filename);
    //上传时间
    String lastModified = String.valueOf(System.currentTimeMillis());
    //附件的模块标识,标识为EB表单的模块
    String module = EntityType.ebuilderform.name();
    //上传完后的物理路径
    String bucketPath = "/oabucket/tj02iu5kla/2024/01/09/09c110e5-7d8e-41f5-b989-7b3c484e4d42";
    //文件大小
    Long size = 12545L;
    //业务id。用于关联某个业务。如：在邮件A上上传几个附件，refId 可以传邮件A的id，
    Long refId = 484895616544L;
    //上传的文件是否生成文档,true 生成，false 不生成
    boolean createDoc = true;
    //目录id,传此参数，必须要生成文档
    Long folderId = 716498491144848L;
    //标识附件来源,操作来源（用于标识，文件是从哪里操作上传，便于特殊业务逻辑处理）目前取值:
    // 1. comment ：来源于评论的附件；2. form 来源表单自定义附件字段；3.relation 来源关联附件字段 4.richText 来源富文本 5. fileAuth 主要用于记录鉴权参数
    String source = "form";
    //物理文件关联
    String url = "09c110e5-7d8e-41f5-b989-7b3c484e4d42";
    //设置物理文件信息
    StoreResults storeResults = new StoreResults();
    //设置租户key
    String tenantKey = "tj02iu5kla";
    storeResults.setCurrentTenantKey(tenantKey);
    //设置物理关联信息
    storeResults.setFileUrl(url);
    //设置存储路径
    storeResults.setFilePath("/oabucket/tj02iu5kla/2024/01/09");
    //设置MD5是否变化，默认false
    storeResults.setMd5Change(false);
    //设置文件名
    storeResults.setFileName(filename);
    //设置文件大小
    storeResults.setFileRealSize(size);
    //设置存储规则id
    StoRuleMatchParam stoRuleMatchParam = new StoRuleMatchParam.MatchBuilder()
            .setEmployeeId(employeeId).extModule(module).setTenantKey(tenantKey).build();
    StoRuleMatchDto stoRuleMatchDto = fileStoRuleMatchClient.match(stoRuleMatchParam);
    storeResults.setRuleVersionId(stoRuleMatchDto.getRuleVersionId());
    //设置存储类型
    FileStorage fileStorage = Optional.ofNullable(stoRuleMatchDto.getFileStorage()).orElse(FileStorage.NONE);
    storeResults.setStorageType(fileStorage.getStorageKey());
    //存储物理文件信息
    uploadClientService.saveStorageInfo(storeResults);
    //构建请求数据
    RemoteUploadParam remoteUploadParam = new RemoteUploadParam(filename,lastModified,module);
    remoteUploadParam.setSize(size);
    remoteUploadParam.setCreateDoc(createDoc);
    remoteUploadParam.setFolderId(folderId);
    remoteUploadParam.setSource(source);
    remoteUploadParam.setUrl(url);
    remoteUploadParam.setRefId(refId);
    remoteUploadParam.setStoreResults(storeResults);
    FileObj fileObj = uploadClientService.remoteUploadSaveData(simpleEmployee,contentType,remoteUploadParam);

}
```

**fileobj 则是文件对象，获取附件id**

Long fileId = fileObj.getId();

### 1.3.3、如何确认附件逻辑数据正常

逻辑数据生成后，会拿到 Fileobj 对象，根据Fileobj 对象中的文件id和url属性在数据库中查询数据

注意：若不清楚是哪个数据库，可以找到 [weaver-file-service.properties](http://weaver-file-service.properties) 配置，查看数据库配置了解

（1）根据文件id检查fileobj 数据是否生成

```java
select * from fileobj where id =
```

(2)根据 url 检查存储信息是否正常生成

```java
select * from file_storage_info where url = ''
```

查询file\_storage\_info 表中 file\_path 的字段是否和【filePath 一致/url】中的 filePath 一致

（3）根据附件id拼成下载接口，进行下载看是否能正常下载

[http://host:port/api/file/remotedownload/{fileId}/upload/true](http://host:port/api/file/remotedownload/{fileId}/upload/true)

# **2、方案二**

## 2.1、适用场景

 （1）文件量不是很大，GB 级别的存储

 （2）适合部分业务场景的迁移

此方案用之前，也请确认好你的E10系统中文件存储确保已经配置好，验证好文件上传下载是否存在异常，否则不要进行后面的迁移工作！！

- 物理文件不便直接迁移到存储中，需要进行上传，但是这个方案遇到大量的文件，尤其是上T，会很依赖你的网络传输速度！！
- 当无法满足方案一的迁移规则进行迁移时，直接选择上传源文件到E10系统
- 你无需再将文件分两份进行处理，调用接口即可完成文件的传输。

## 2.2、迁移示例

```java
引入依赖

<dependency>
   <groupId>com.weaver</groupId>
   <artifactId>weaver-file-upload-download</artifactId>
</dependency>

@Autowired
private FileUploadService fileUploadService;

@GetMapping("/test2")
public void test(){
    //第三方文件来源（第三方文件来源需要根据实际存储情况获取，本示例以第三方的文件存储在本地磁盘为例）
    String rootPath = "/nfs/data/abc.txt";
    File file = new File(rootPath);
    //必须要指定一名E10系统的用户。
    Long employeeId = 4584875646494498L;
    //文件头，具体可参考 https://blog.csdn.net/qq_35957643/article/details/119203983 ，若不清楚，可不传
    String contentType = "test/plan";
    //文件名，需要强行指定
    String filename = "abc.txt";
    //上传时间搓，可不指定
    String lastModified = String.valueOf(System.currentTimeMillis());
    //文件归属来源。必须指定 EntityType 中已经存在的模块来源,需要根据实际归属的模块指定，不要乱指定模块
    String module = EntityType.document.name();
    RemoteUploadParam remoteUploadParam = new RemoteUploadParam(filename,lastModified,module);
    //开始上传
    FileObj fileObj = fileUploadService.uploadLocalFile(file,employeeId,contentType,remoteUploadParam);
}
```

查询文件是否传入成功，可按照方案一第1.3.3 点进行验证。

注意：本示例只体格了一种上传方式，同时支持不同文件源的上传方式，具体文档可参考  
sdk 上传文档：

【https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/cd16e1dc-75be-4848-88ae-092fe1d0f8ee】

# 3、方案三

## 3.1、适用场景

 适合第三方系统在使用中将文件陆续迁移到E10 系统中

## **3.2、迁移思路**

** 使用文件采集功能直接采集第三方系统中的文件到E10系统中，请参考如下文档：**

** 【**[https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/49d67e61-5a2d-4d07-9340-7fde68f5ed7c](https://weapp.eteams.cn/build/techdoc/wdoc/index.html#/public/doc/49d67e61-5a2d-4d07-9340-7fde68f5ed7c)**】**

# 4、迁移场景&文件如何和业务关联说明

 业务模块通过两种方式关联文件

1、业务模块记录文件id，来展示文件id

2、业务模块在上传的时候设置 refId 的值，通过此值查询文件并展示文件

 场景说明：

示例（1）：将文件迁移到E10，并在流程表单中展示

选择方案1或者方案2迁移完成后获取到Fileobj 对象，并拿到文件id，再找表单确认是如何关联文件的，如果是关联的文件id，那么在迁移完成文件id 后，需要流程提供接口，去关联文件id 。

 示例（2）：将文件迁移到文档。并在文档中显示，文档的数据已经生成，需要将附件关联到文档下

选择方案1，生成逻辑数据的时候，将module 参数设置为document，refId 设置为文档id。

或者方案2，上传文件的时候将module 参数设置为document，refId 设置为文档id，并上传

###
