nas-grpc-filesytem 2.0
一个使用gRPC客户端的Python文件处理器，用于处理ctec文件工作，
License: MIT
Requires Python: >=2.7

# NASGrpcFileSystem3 - Python文件上传下载功能包

## 1. 功能包介绍

NAS文件操作系统 - Grpc版本Python包，支持Python 2.7及以上版本

## 2. 环境配置

### 部署方式

- 建议使用连接复用方式初始化连接
- 暂不支持gunicorn部署

### 服务地址

- 准一环境：`172.16.20.30:31508`
- 生产环境：`P128010302.dzqd.lio:31508`

## 3. 接口说明

### 3.1 创建文件接口

#### 参数说明

| 参数名           | 类型              | 作用                                         | 是否可选 | 关联参数             |
|---------------|-----------------|--------------------------------------------|------|------------------|
| caller_code   | String          | 调用方编码                                      | 否    | -                |
| remote_path   | String          | 远程存储目录                                     | 否    | -                |
| x_type        | String          | 业务类型，若mount_path为空，则根据业务类型存储到不同的服务器mount目录 | 否    | -                |
| req_url       | String          | 请求IP:PORT                                  | 是    | 使用连接复用方式时不需要传此参数 |
| file          | String / base64 | 文件流，可以是文件路径、文件流或base64编码的文件                | 是    | filename         |
| filename      | String          | 文件名称，当上传对象为文件流时必传                          | 是    | file             |
| file_path     | String          | 本地文件路径，可以是文件路径或文件流                         | 是    | -                |
| mount_path    | String          | 服务器mount地址                                 | 是    | -                |
| replace       | Boolean         | 当文件已在服务器存在时是否强制替换，默认替换                     | 是    | -                |
| recovery_path | String          | 容灾路径，当文件上传非正常失败时将文件存储到本地地址                 | 是    | -                |
| timeout       | Int             | 超时时间，默认3秒                                  | 是    | -                |
| logger        | Object          | 日志对象                                       | 是    | -                |

#### 返回参数

- 返回格式：元组 `(错误码, 错误描述, mount地址)`
- 错误码为0时：错误描述为成功说明，mount地址为服务器存储地址
- 错误码非0时：错误描述为错误说明，mount地址为空字符串
- 示例：`(0, '成功', '/picture/')`

### 3.2 返回文件接口

#### 参数说明

| 参数名                | 类型     | 作用                                  | 是否可选 | 关联参数             |
|--------------------|--------|-------------------------------------|------|------------------|
| caller_code        | String | 调用方编码                               | 否    | -                |
| remote_full_path   | String | 远程文件存储路径及完整文件名称（全地址）                | 否    | -                |
| recovery_full_path | String | 容灾目录文件，当非正常错误发生时读取此文件               | 是    | -                |
| req_url            | String | 请求IP:PORT                           | 是    | 使用连接复用方式时不需要传此参数 |
| local_full_path    | String | 下载的文件本地存储路径（包含文件名，全地址）              | 是    | return_file      |
| return_file        | String | 是否需要将文件流返回，如不返回则需要传入local_full_path | 是    | local_full_path  |
| timeout            | Int    | 超时时间，默认3秒                           | 是    | -                |
| logger             | Object | 日志对象                                | 是    | -                |

#### 返回参数

- 返回格式：元组 `(错误码, 错误描述, 文件流)`
- 错误码非0时：错误描述为错误说明
- 文件流返回情况：
    1. 若获取不到容灾目录文件（入参为空或文件不存在），返回文件流为`""`
    2. 若获取到了容灾目录文件，而入参`return_file=False`，则返回文件流为`""`
    3. 若获取到了容灾目录文件，而入参`return_file=True`，则返回读取到的文件流

### 3.3 修改文件名称接口

#### 参数说明

| 参数名                | 类型      | 作用                        | 是否可选 | 关联参数             |
|--------------------|---------|---------------------------|------|------------------|
| caller_code        | String  | 调用方编码                     | 否    | -                |
| remote_full_path   | String  | 远程文件存储路径及完整文件名称（全地址）      | 否    | -                |
| req_url            | String  | 请求IP:PORT                 | 是    | 使用连接复用方式时不需要传此参数 |
| modified_file_name | String  | 修改后的文件名称                  | 否    | -                |
| recovery_full_path | String  | 容灾目录文件，当非正常错误发生时修改此文件名称   | 是    | -                |
| replace            | Boolean | 如果服务器已有要改的文件名，是否强制覆盖，默认覆盖 | 是    | -                |
| timeout            | Int     | 超时时间，默认3秒                 | 是    | -                |
| logger             | Object  | 日志对象                      | 是    | -                |

#### 返回参数

- 返回格式：元组 `(错误码, 错误描述)`
- 示例：`(0, '操作成功')`

### 3.4 复制文件接口

#### 参数说明

| 参数名                | 类型     | 作用                                              | 是否可选 | 关联参数             |
|--------------------|--------|-------------------------------------------------|------|------------------|
| caller_code        | String | 调用方编码                                           | 否    | -                |
| original_full_path | String | 远程文件存储路径及完整文件名称（全地址）                            | 否    | -                |
| req_url            | String | 请求IP:PORT                                       | 是    | 使用连接复用方式时不需要传此参数 |
| new_full_path      | String | 复制后的文件路径（传文件名则变更文件名称，不传则为原文件名称）；路径存在直接移动，不存在则创建 | 否    | -                |
| timeout            | Int    | 超时时间，默认3秒                                       | 是    | -                |
| logger             | Object | 日志对象                                            | 是    | -                |

#### 返回参数

- 返回格式：元组 `(错误码, 错误描述)`
- 示例：`(0, '操作成功')`

### 3.5 移动文件接口

#### 参数说明

| 参数名                | 类型     | 作用                                               | 是否可选 | 关联参数             |
|--------------------|--------|--------------------------------------------------|------|------------------|
| caller_code        | String | 调用方编码                                            | 否    | -                |
| original_full_path | String | 远程文件存储路径及完整文件名称（全地址）                             | 否    | -                |
| req_url            | String | 请求IP:PORT                                        | 是    | 使用连接复用方式时不需要传此参数 |
| new_full_path      | String | 移动后的文件存储路径（存在直接移动，不存在则创建），不包含文件名（移动后文件名称与原文件名一致） | 否    | -                |
| timeout            | Int    | 超时时间，默认3秒                                        | 是    | -                |
| logger             | Object | 日志对象                                             | 是    | -                |

#### 返回参数

- 返回格式：元组 `(错误码, 错误描述)`
- 示例：`(0, '操作成功')`

## 4. 错误码说明

| 错误码    | 错误码描述    | 备注 |
|--------|----------|----|
| 0      | 操作成功     |    |
| 1      | 请求失败     |    |
| 128501 | 参数异常     |    |
| 128502 | 缺少请求必传参数 |    |
| 128503 | 文件缺少扩展名  |    |
| 128504 | 目录不存在    |    |
| 128505 | 文件不存在    |    |
| 128506 | 不是文件     |    |
| 128507 | 文件已存在    |    |
| 128508 | 目录已存在    |    |
| 128509 | 读取超时     |    |
| 128510 | 保存文件失败   |    |
| 128511 | 文件夹创建失败  |    |
| 128512 | 连接超时     |    |
| 128513 | 读取文件失败   |    |
| 128514 | 权限不足     |    |
| 128515 | 业务场景不存在  |    |

## 5. 安装与使用

### 安装

```bash
pip install NASGrpcFileSystem3
```

### 使用示例

```python
# coding: utf-8
import base64
from future.utils import raise_
from NASGrpcFileSystem3 import create_file, describe_file, modify_file, copy_file, move_file, FileHandler


def gen_file(file_path, size=1024 * 1024):
    """
    文件流生成器
    :param file_path: 文件路径
    :param size: 每次读取大小，默认1MB
    :return: 文件流生成器
    """
    try:
        with open(file_path, 'rb') as f:
            data = f.read(size)
            while data:
                yield data
                data = f.read(size)
    except Exception as exc:
        raise_(Exception, exc)


def img_to_64(path):
    """
    图片Base64编码
    :param path: 文件路径
    :return: Base64编码后的文件内容
    """
    after_base64 = None
    try:
        with open(path, 'rb') as f:
            after_base64 = base64.b64encode(f.read())
    except Exception as exc:
        raise_(Exception, exc)
    return after_base64


def call_create_file(caller_code, remote_path, x_type, req_url, file, filename, mount_path, recovery_path=None):
    """创建文件（单次连接方式）"""
    try:
        res = create_file(caller_code, remote_path, x_type, req_url, file, filename, file_path=None,
                          mount_path=mount_path, replace=True, recovery_path=recovery_path, timeout=3, logger=None)
    except Exception as exc:
        res = (1, '', '')
    return res


def call_create_file_path(caller_code, remote_path, x_type, req_url, file_path, mount_path, recovery_path="/demo"):
    """通过文件路径创建文件（单次连接方式）"""
    try:
        res = create_file(caller_code, remote_path, x_type, req_url, file_path=file_path, mount_path=mount_path,
                          recovery_path=recovery_path, timeout=30, logger=None)
    except Exception as exc:
        res = (1, '', '')
    return res


def call_describe_file(caller_code, remote_full_path, req_url, local_full_path, recovery_full_path):
    """获取文件（单次连接方式）"""
    try:
        res = describe_file(caller_code, remote_full_path, req_url, local_full_path,
                            recovery_full_path, return_file=True, timeout=3, logger=None)
    except Exception as exc:
        res = (1, '', '')
    return res


def call_modify_file(caller_code, remote_full_path, req_url, modified_file_name, recovery_full_path=None):
    """修改文件名称（单次连接方式）"""
    try:
        res = modify_file(caller_code, remote_full_path, req_url, modified_file_name,
                          recovery_full_path=recovery_full_path, timeout=3, logger=None)
    except Exception as exc:
        res = (1, '')
    return res


def call_copy_file(caller_code, original_full_path, req_url, new_file_path):
    """复制文件（单次连接方式）"""
    try:
        res = copy_file(caller_code, original_full_path, req_url, new_file_path, timeout=30, logger=None)
    except Exception as exc:
        res = (1, '')
    return res


def call_move_file(caller_code, original_full_path, req_url, new_file_path):
    """移动文件（单次连接方式）"""
    try:
        res = move_file(caller_code, original_full_path, req_url, new_file_path, timeout=30, logger=None)
    except Exception as exc:
        res = (1, '')
    return res


if __name__ == "__main__":
    req_url = ""  # 使用连接复用方式时可不传
    caller_code = "test_python_sdk"

    # 准备文件流
    try:
        file_ = gen_file("./test/c1.jpg", 2048)
        # file_ = img_to_64("./test/c1.jpg")  # Base64方式
    except Exception as exc:
        file_ = " "

    #### 连接复用方式（推荐）###################
    grpc_handler = FileHandler(req_url)

    # 创建文件
    res_create = grpc_handler.create_file(caller_code, "test/", "m1180", file_path="./test/c1.jpg",
                                          mount_path="/picture/")

    # 获取文件
    res_desc = grpc_handler.describe_file(caller_code, '/picture/test/g2.jpg', "./ccc.jpg")

    # 修改文件
    res_modify = grpc_handler.modify_file(caller_code, '/picture/test/g2.jpg', "g1.jpg")

    # 复制文件
    res_copy = grpc_handler.copy_file(caller_code, '/picture/test/g1.jpg', "/picture/test_move1/g1.jpg")

    # 移动文件
    res_move = grpc_handler.move_file(caller_code, '/picture/test_move1/g1.jpg', "/picture/test")

    #### 单次连接方式 ###################
    # 创建文件
    res_up = call_create_file(caller_code, "test/", "m1180", req_url, file_, "c1.jpg", "/picture/")
    print(res_up)

    res_up = call_create_file_path(caller_code, "test/", "m1180", req_url, "./test/c1.jpg", "/picture/")
    print(res_up)

    # 获取文件
    res_down = call_describe_file(caller_code, '/picture/test/c1.jpg', req_url, "./g0.jpg", None)
    print(res_down)

    # 修改文件名称
    res_modify = call_modify_file(caller_code, '/picture/test/c1.jpg', req_url, "c2.jpg", "./test/c1.jpg")
    print(res_modify)

    # 复制文件
    res_copy = call_copy_file(caller_code, '/picture/test/c2.jpg', req_url, "/picture/test_move1/copyc1.jpg")
    print(res_copy)

    # 移动文件
    res_move = call_move_file(caller_code, '/picture/test_move1/copyc1.jpg', req_url, "/picture/test")
    print(res_move)
```