
# MoXing基础功能
#### 场景描述
在使用ModelArts服务时，您可能会遇到需要访问对象存储服务（OBS）的情况。OBS是一个基于对象的海量存储服务，与传统的本地文件系统不同，无法直接通过文件路径访问OBS上的文件。
直接访问OBS文件时会遇到以下问题：
- 无法像访问本地文件那样使用open()方法。
- 需要通过网络请求进行文件读写操作。
- 使用OBS Python SDK接口可能较为复杂。
如何在ModelArts服务中更便捷地访问和操作OBS文件？
ModelArts提供了mox.file接口，为OBS操作提供了更简便的解决方案。以下是使用mox.file接口访问OBS文件的示例：
```
# 访问OBS文件示例
import moxing as mox
# 打开OBS文件
with mox.file.File('obs://bucket_name/a.txt', 'r') as f:
    print(f.read())
# 列举OBS目录
mox.file.list_directory('obs://bucket_name/my_dir/')
```
**使用mox.file接口注意事项**
1. mox.file接口主要适用于提升从OBS读取和下载数据的易用性。
2. 对于OBS并行文件系统部分接口可能存在兼容性问题，建议直接使用OBS Python SDK进行生产业务开发。
3. 详细接口说明请参见《对象存储服务OBS》的[Python SDK接口概览](https://support.huaweicloud.com/sdk-python-devg-obs/obs_22_0100.html)。
通过mox.file接口，您可以像操作本地文件一样便捷地访问和管理OBS资源，从而提高开发效率。
#### 约束限制
- 需要访问的OBS桶需要满足当前训练作业，可以正常访问。
- 当前账户有读写OBS桶权限。
- 使用ModelArts标准镜像或基于自定义镜像安装**moxing**库。
 
#### 文件复制
- 复制一个文件。**mox.file.copy** 仅支持对文件操作，如果要对文件夹操作，请使用**mox.file.copy_parallel** 。
  - 从OBS复制到OBS，例如将"obs://bucket_name/obs_file.txt"复制到"obs://bucket_name/obs_file_2.txt"。
    ```
    import moxing as mox
    mox.file.copy('obs://bucket_name/obs_file.txt', 'obs://bucket_name/obs_file_2.txt')
    ```
    
  
  - 将OBS文件复制到本地，也就是下载一个OBS文件。例如下载"obs://bucket_name/obs_file.txt"到本地"/tmp/obs_file.txt"。
    ```
    import moxing as mox
    mox.file.copy('obs://bucket_name/obs_file.txt', '/tmp/obs_file.txt')
    ```
    
  
  - 将本地文件复制到OBS，也就是上传一个OBS文件，例如上传"/tmp/obs_file.txt"到"obs://bucket_name/obs_file.txt"。
    ```
    import moxing as mox
    mox.file.copy('/tmp/obs_file.txt', 'obs://bucket_name/obs_file.txt')
    ```
    
  
  - 将本地文件复制到本地，操作等价于**shutil.copyfile** ，例如将"/tmp/obs_file.txt"复制到"/tmp/obs_file_2.txt"。
    ```
    import moxing as mox
    mox.file.copy('/tmp/obs_file.txt', '/tmp/obs_file_2.txt')
    ```
    
   
- 对于体积较大的文件，**mox.file.copy**会默认使用分片并发下载方式进行加速，相关参数可以通过环境变量控制。
  1. 判断文件是否为大文件，超过该阈值会启用分片并发下载。
     ```
     MOX_FILE_PARTIAL_MAXIMUM_SIZE
     ```
     默认大小为5GB，设置单位为Byte，例如设置5GB需要写为5368709120。
     
  
  2. 管理文件分片大小。
     ```
     MOX_FILE_LARGE_FILE_PART_SIZE
     ```
     默认大小为10MB，设置单位为Byte。由于OBS限制分片数量最多为10000片，当文件大小超过100G时需要上调分片大小。
     
  
  3. 管理下载并发多进程数量。下载并发多进程数，表示大文件下载时会启动几个线程进行下载，新版本中默认值为8，节点较少或者大文件数量较少时，可以适当扩大并发数，提升下载性能。
     ```
     MOX_FILE_LARGE_FILE_TASK_NUM
     ```
     默认大小为32，当并发数过高导致OBS桶流控时需要调小。
     
   
- 复制一个文件夹。**mox.file.copy_parallel** 仅支持对文件夹操作，如果要对文件操作，请使用**mox.file.copy** 。
  - 从OBS复制到OBS，例如将obs://bucket_name/sub_dir_0复制到obs://bucket_name/sub_dir_1
    ```
    import moxing as mox
    mox.file.copy_parallel('obs://bucket_name/sub_dir_0', 'obs://bucket_name/sub_dir_1')
    ```
    
  
  - 将OBS文件夹复制到本地，也就是下载一个OBS文件夹。例如下载"obs://bucket_name/sub_dir_0"到本地"/tmp/sub_dir_0"。
    ```
    import moxing as mox
    mox.file.copy_parallel('obs://bucket_name/sub_dir_0', '/tmp/sub_dir_0')
    ```
    
  
  - 将本地文件夹复制到OBS，也就是上传一个OBS文件夹，例如上传"/tmp/sub_dir_0"到"obs://bucket_name/sub_dir_0"。
    ```
    import moxing as mox
    mox.file.copy_parallel('/tmp/sub_dir_0', 'obs://bucket_name/sub_dir_0')
    ```
    
  
  - 将本地文件夹复制到本地，操作等价于**shutil.copytree** ，例如将"/tmp/sub_dir_0"复制到"/tmp/sub_dir_1"。
    ```
    import moxing as mox
    mox.file.copy_parallel('/tmp/sub_dir_0', '/tmp/sub_dir_1')
    ```
    
   
 
**mox.file.copy_parallel**可以通过threads参数控制复制操作的并发数，该并发数默认为16。
file_list参数可以选择需要复制的文件。例如上传"/tmp/sub_dir_0"中的"/tmp/sub_dir_0/train/1.jpg"和"/tmp/sub_dir_0/eval/2.jpg"到"obs://bucket_name/sub_dir_0"。
```
import moxing as mox
mox.file.copy_parallel('/tmp/sub_dir_0', 'obs://bucket_name/sub_dir_0', file_list=['train/1.jpg', 'eval/2.jpg'])
```
#### 读写操作
- 读取一个OBS文件。
  例如读取"obs://bucket_name/obs_file.txt"文件内容，返回string（字符串类型）。
  ```
  import moxing as mox
  file_str = mox.file.read('obs://bucket_name/obs_file.txt')
  ```
  也可以使用打开文件对象并读取的方式来实现，两者是等价的。
  ```
  import moxing as mox
  with mox.file.File('obs://bucket_name/obs_file.txt', 'r') as f:
    file_str = f.read()
  ```
  
- 从文件中读取一行，返回string，以换行符结尾。同样可以打开OBS的文件对象。
  ```
  import moxing as mox
  with mox.file.File('obs://bucket_name/obs_file.txt', 'r') as f:
    file_line = f.readline()
  ```
  
- 从文件中读取所有行，返回一个list，每个元素是一行，以换行符结尾。
  ```
  import moxing as mox
  with mox.file.File('obs://bucket_name/obs_file.txt', 'r') as f:
    file_line_list = f.readlines()
  ```
  
- 以二进制模式读取一个OBS文件。
  例如读取"obs://bucket_name/obs_file.bin"文件内容，返回bytes（字节类型）。
  ```
  import moxing as mox
  file_bytes = mox.file.read('obs://bucket_name/obs_file.bin',binary=True)
  ```
  也可以使用打开文件对象并读取的方式来实现，两者是等价的。
  ```
  import moxing as mox
  with mox.file.File('obs://bucket_name/obs_file.bin', 'rb') as f:
    file_bytes = f.read()
  ```
  以二进制模式打开的文件也支持读取一行或者读取所有行，用法不变。
  
- 通过环境变量MOX_SLOW_FUNC_STATS可以选择统计的调用函数与其耗时阈值，超过该阈值的会打印日志。该环境变量可以设置为OBS getObject，MemArts CC read，MemArts CC batch read，OBS getObjectMetadata，OBS listObjects，Metadata cache exist， mount read其中的一个或多个，后续带有：和阈值大小。不同函数以；隔开。例如设置MOX_SLOW_FUNC_STATS="MemArts CC read:1.0;OBS getObject:1.2"，则检测MemArts CC read中超过1.0s的请求和OBS GETOBJECT操作中超过1.2s的请求。
- 将字符串写入一个文件（不支持写入超过2G的内容）。
  例如将字符串"Hello World!"写入OBS文件"obs://bucket_name/obs_file.txt"中。
  ```
  import moxing as mox
  mox.file.write('obs://bucket_name/obs_file.txt', 'Hello World!')
  ```
  也可以使用打开文件对象并写入的方式来实现，两者是等价的。
  ```
  import moxing as mox
  with mox.file.File('obs://bucket_name/obs_file.txt', 'w') as f:
    f.write('Hello World!')
  ```
  ![](https://support.huaweicloud.com/usermanual-standard-modelarts/public_sys-resources/note_3.0-zh-cn.png)
  用写入模式打开文件或者调用mox.file.write时，如果被写入文件不存在，则会创建，如果已经存在，则会覆盖。
  
- 追加一个OBS文件。 例如将字符串"Hello World!"追加到"obs://bucket_name/obs_file.txt"文件中。
  ```
  import moxing as mox
  mox.file.append('obs://bucket_name/obs_file.txt', 'Hello World!')
  ```
  也可以使用打开文件对象并追加的方式来实现，两者是等价的。
  ```
  import moxing as mox
  with mox.file.File('obs://bucket_name/obs_file.txt', 'a') as f:
    f.write('Hello World!')
  ```
  用追加模式打开文件或者调用**mox.file.append**时，如果被写入文件不存在，则会创建，如果已经存在，则直接追加。
  当被追加的源文件比较大时，例如"obs://bucket_name/obs_file.txt"文件大小超过5MB时，追加一个OBS文件的性能较低。
  ![](https://support.huaweicloud.com/usermanual-standard-modelarts/public_sys-resources/note_3.0-zh-cn.png)
  如果以写入模式或追加模式打开文件，当调用write方法时，待写入内容暂时的被存在的缓冲区，直到关闭文件对象（退出with语句时会自动关闭文件对象）或者主动调用文件对象的close()方法或flush()方法时，文件内容才会被写入。
  
 
#### 列举操作
- 列举一个OBS目录，只返回顶层结果（相对路径），不做递归列举。 例如列举"obs://bucket_name/object_dir"，返回该目录下所有的文件和文件夹，不会递归查询。
  假设"obs://bucket_name/object_dir"中有如下结构
  ```
  bucket_name
        |- object_dir
          |- dir0
            |- file00
          |- file1
  ```
  调用如下代码：
  ```
  import moxing as mox
  mox.file.list_directory('obs://bucket_name/object_dir')
  ```
  返回一个list：
  ```
  ['dir0', 'file1']
  ```
  
- 递归列举一个OBS目录，返回目录中所有的文件和文件夹（相对路径），并且会做递归查询。 假设obs://bucket_name/object_dir中有如下结构。
  ```
  bucket_name
        |- object_dir
          |- dir0
            |- file00
          |- file1
  ```
  调用如下代码：
  ```
  import moxing as mox
  mox.file.list_directory('obs://bucket_name/object_dir', recursive=True)
  ```
  返回一个list：
  ```
  ['dir0', 'dir0/file00', 'file1']
  ```
  
 
#### 创建文件夹
创建一个OBS目录（即OBS文件夹）。支持递归创建，即如果"sub_dir_0"文件夹不存在，也会自动被创建，如果已经存在，则不起任何作用。
```
import moxing as mox
mox.file.make_dirs('obs://bucket_name/sub_dir_0/sub_dir_1')
```
#### 查询操作
- 判断一个OBS文件是否存在，如果存在则返回**True** ，如果不存在则返回**False** 。
  ```
  import moxing as mox
  mox.file.exists('obs://bucket_name/sub_dir_0/file.txt')
  ```
  
- 判断一个OBS文件夹是否存在，如果存在则返回**True** ，如果不存在则返回**False** 。
  ```
  import moxing as mox
  mox.file.exists('obs://bucket_name/sub_dir_0/sub_dir_1')
  ```
  ![](https://support.huaweicloud.com/usermanual-standard-modelarts/public_sys-resources/note_3.0-zh-cn.png)
  由于OBS允许同名的文件和文件夹（Unix操作系统不允许），如果存在同名的文件和文件夹，例如"obs://bucket_name/sub_dir_0/abc"，当调用mox.file.exists时，不论abc是文件还是文件夹，都会返回True。
  
- 判断一个OBS路径是否为文件夹，如果是则返回**True** ，否则返回**False** 。
  ```
  import moxing as mox
  mox.file.is_directory('obs://bucket_name/sub_dir_0/sub_dir_1')
  ```
  ![](https://support.huaweicloud.com/usermanual-standard-modelarts/public_sys-resources/note_3.0-zh-cn.png)
  由于OBS允许同名的文件和文件夹（Unix操作系统不允许），如果存在同名的文件和文件夹，例如obs://bucket_name/sub_dir_0/abc，当调用mox.file.is_directory时，会返回True。
  
- 获取一个OBS文件的大小，单位为bytes。
  例如获取"obs://bucket_name/obs_file.txt"的文件大小。
  ```
  import moxing as mox
  mox.file.get_size('obs://bucket_name/obs_file.txt')
  ```
  
- 递归获取一个OBS文件夹下所有文件的大小，单位为bytes。
  例如获取"obs://bucket_name/object_dir"目录下所有文件大小的总和。
  ```
  import moxing as mox
  mox.file.get_size('obs://bucket_name/object_dir', recursive=True)
  ```
  
- 获取一个OBS文件或文件夹的stat信息，stat信息中包含如下信息。
  - length：文件大小。
  
  - mtime_nsec：创建时间戳。
  
  - is_directory：是否为目录。
  
  
  例如查询一个OBS文件"obs://bucket_name/obs_file.txt"，此文件地址也可以替换成一个文件夹地址。
  ```
  import moxing as mox
  stat = mox.file.stat('obs://bucket_name/obs_file.txt')
  print(stat.length)
  print(stat.mtime_nsec)
  print(stat.is_directory)
  ```
  
 
#### 删除操作
- 删除一个OBS文件。
  例如删除"obs://bucket_name/obs_file.txt"。
  ```
  import moxing as mox
  mox.file.remove('obs://bucket_name/obs_file.txt')
  ```
  
- 删除一个OBS目录，并且递归的删除这个目录下的所有内容。如果这个目录不存在，则会报错。
  例如删除"obs://bucket_name/sub_dir_0"下的所有内容。
  ```
  import moxing as mox
  mox.file.remove('obs://bucket_name/sub_dir_0', recursive=True)
  ```
  
 
#### 设置参数
- 使用mox.file.set_auth函数设置所有可配置的参数。
  ```
  #配置moxing读取的参数，具体如下，对于不需要修改配置项不用显式指定即可，该API全局执行一次，不能多次执行 
  #ak - Access Key ， 字符串类型，在ModelArts使用时，系统默认配置，其他环境参考统一身份认证
  #sk - Secret Access Key，字符串类型，在ModelArts使用时，系统默认配置，其他环境参考统一身份认证
  #server - OBS 服务端， 在ModelArts使用时，系统默认配置，其他环境配置参考https://support.huaweicloud.com/productdesc-obs/obs_03_0152.html
  #port - OBS服务器端口， 在ModelArts使用时，系统默认配置，其他环境配置参考server，一般不会给定特殊端口号，不需要配置
  #is_secure - 是否使用https, 布尔类型，默认值为True
  #ssl_verify - 是否使用ssl验证，布尔类型，默认值为False
  #long_conn_mode - 是否使用长连接模式，布尔类型，默认配置为True
  #path_style - 是否使用路径风格. 布尔类型，HEC则默认设置为True，当为公有云则默认设置为False，默认不建议修改
  #retry - 总共尝试的次数，类型为整型，默认值为10，单位为次
  #retry_wait - 在每次尝试时所等待的时间，类型为浮点型，默认值为0.1，单位秒，如果不配置，则第一次等待0.1秒，第二次失败等待0.2秒，第三次为0.4秒，指数递增#client_timeout - obs客户端超时时间， 类型为整型，默认值为30， 单位秒#list_max_keys - 每页列出的最大对象数，用于list_directory api，类型为整型，默认值为1000， 单位为个 
  #timeout_config - 设置moxing各个函数的默认超时时间列表，单位为秒，默认为
  {
    'read': 60,  # read函数超时时间
    'listObjects': 10,  # list_directory函数列举1000个对象的超时时间
    'deleteObject': 60,  # rename，remove函数删除对象的超时时间
    'getObject': 60,  # copy函数本地获取obs对象的超时时间
    'getObjectMetadata': 30,  #copy，read等函数获取obs对象元数据的超时时间
    'putFile': 120,  # copy函数本地上传obs小于5G文件的超时时间
    'putContent': 30,  # copy_parallel函数创建obs目录超时时间
    'copyPart': 120,  # copy函数obs之间复制文件分片超时时间
    'copyObject': 120,  # copy函数obs之间复制超时时间
    'uploadPart': 120,  # copy函数本地上传obs文件分片的超时时间
    'initiateMultipartUpload': 30,  # copy函数本地上传obs大于5G文件的初始化超时时间
    'completeMultipartUpload': 120,  # copy函数本地上传obs小于5G文件的完成超时时间
  } # 当遇到对应场景超时时，可修改对应函数的超时时间
  import moxing as mox 
  mox.file.set_auth(ak='xxx', sk='xxx')
  ```
  
- 使用mox.file.reset_auth重置aksk，moxing 2.6版本后支持。默认情况下ma会预置一个最终租户的aksk，但是调用set_auth可以改成其他租户的aksk。如果想继续使用默认的最终租户aksk，可调用reset_auth重置aksk。
  ```
  import moxing as mox 
  mox.file.reset_auth()
  ```
  
 
