文档中心 > 开发指南

快速上手文件下载

更新时间:2024/08/21 访问次数:1821

一、配置

参考文章:链接

参考文章《外部域名添加》的开通云服务添加章节。

二、空小游戏项目发起http请求

1. 创建项目

后台创建项目

参考文章:链接

参考文章的第一章。

将域名g.alicdn.com配置到后台上,下文会用到;

IDE创建项目

选择对应的项目,然后选择设置项目保存路径,点击【确定】。

2. 开发

安装云服务的SDK

1
npm install @tbmp/mp-cloud-sdk


引入库

在game.js的第一行引入

1
import cloud from '@tbmp/mp-cloud-sdk';

定义测试函数

注意:代码中所涉及到的测试资源仅供测试参考,不能用于生产环境。

代码中设计的http请求详情可以阅读《快速上手Http请求》文章;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
const fs = my.getFileSystemManager();
 
const createDir = (targetPath) => {
  const mkdirResult = fs.mkdirSync({ dirPath: targetPath, recursive: true });
  console.log('创建目录:', targetPath, '结果', mkdirResult);
}
 
const getFileMD5 = (filePath) => {
  const fileInfo = fs.getFileInfoSync({ filePath, digestAlgorithm: 'md5' });
  return fileInfo?.digest;
}
 
const readUTF8File = (filePath) => {
  fs.readFile({
    filePath,
    encoding: 'utf-8', // 如果不传 encoding,则以 ArrayBuffer 格式读取文件的二进制内容
    success: (textData) => {
      console.log('文本的内容', textData);
    }
  });
}
 
const requestConfig = () => {
  return new Promise((res, rej) => {
 
    cloud.init({
      //test、online
      env: 'test'
    });
    const result = cloud.application.httpRequest({
      //不需要完整域名,只需要接口访问路径即可
      path: '/hdtest/igo_assets_test/0.6.0/json/assets.json',
      'method': 'GET',
      'headers': {},
 
      'exts': {
        "timeout": 1000,
        //空应用调用需要填写该字段,包括协议头以及端口号(可省略),支持http、https
        "domain": "https://g.alicdn.com"
      }
    }).then((httpRequestRes) => {
      console.log('requestConfig', httpRequestRes);
      res(JSON.parse(httpRequestRes));
    }).catch((httpRequestError) => {
      rej(httpRequestError);
    });
 
 
 
  });
 
}
 
// 在线生成文件md5码
const readTextPromise = () => {
  return new Promise((res, rej) => {
    const fileName = 'utf8_arraybuffer_base64';
    const filePath = `${my.env.USER_DATA_PATH}/data/${fileName}`;
 
    requestConfig().then((requestConfigRes) => {
      console.log('requestConfig res=', requestConfigRes);
      const configData = requestConfigRes;
      const localFileMD5 = getFileMD5(filePath);
      const remoteFileMD5 = configData.find(item => item.name === fileName)?.md5;
 
      console.log('测试', localFileMD5, remoteFileMD5)
 
      //  如果md5不一致,则进行文件下载
      if (localFileMD5 === remoteFileMD5) {
        console.log('本地文件md5一致,无需下载');
        readUTF8File(filePath);
        res(true);
      } else {
        console.log('本地文件md5不一致,需要下载');
        my.downloadFile({
          url: 'https://g.alicdn.com/hdtest/igo_assets_test/0.4.0/bin/' + fileName,
          success(data) {
            const textPath = data?.apFilePath;
 
            createDir(`${my.env.USER_DATA_PATH}/data/`);
 
            fs.copyFile({
              srcPath: textPath,
              destPath: filePath,
              success: (copyFileRes) => {
                console.log("拷贝文件成功", copyFileRes);
                readUTF8File(filePath);
                res(true);
              },
              fail: (copyFileError) => {
                console.log("拷贝文件失败", copyFileError);
                rej(false);
              }
            });
 
          },
          fail(res) {
            console.error('text报错', res.errorMessage || res.error);
            rej(false);
          },
        })
      }
    }).catch((loadAssetError) => {
      console.error('下载asset.json失败', loadAssetError);
      rej(false);
    })
 
  });
};
 
const targetPath = `${my.env.USER_DATA_PATH}/unzipDir02`;
const unzipPromise = () => {
  return new Promise((res, rej) => {
    my.downloadFile({
      success(data) {
        try {
          const path = data?.apFilePath;
 
          const rmdir = fs.rmdirSync({ dirPath: targetPath, recursive: true });
          console.log('rmdir', rmdir);
          createDir(targetPath);
          fs.unzip({
            zipFilePath: path,
            targetPath: targetPath,
            success: function (unzipResult) {
              console.log('解压成功', unzipResult);
 
              try {
                // 存储空间有限,需要手动维护空间,若不处理及时处理会导致存储空间超过200mb限制,文件操作会失败
                fs.unlink({
                  filePath: path,
                  success: unlinkResult => console.log('删除zip包成功', unlinkResult),
                  fail: unlinkError => console.error('删除zip包失败', unlinkError)
                });
              } catch (error) { }
 
              const fileList = fs.readdirSync({ dirPath: targetPath });
              console.log('fileList', fileList);
              const unzipFileInfo = fs.getFileInfoSync({ filePath: targetPath + '/' + fileList?.files?.[0] });
              console.log('unzipFileInfo', unzipFileInfo);
 
              res(true);
            },
            fail: function (err) {
              console.log('解压失败', err);
              rej(false)
            }
          });
        } catch (error) {
          console.log('解压前准备工作失败', error);
          rej(false)
        }
      },
      fail(res) {
        console.error('下载报错', res.errorMessage || res.error);
        rej(false);
      },
    })
  });
}
 
const testErrorCode10028 = () => {
  const testErrorPath = `${my.env.USER_DATA_PATH}/testError`
  return new Promise((res, rej) => {
    my.downloadFile({
      success(data) {
        try {
          const path = data?.apFilePath;
 
          const rmdir = fs.rmdirSync({ dirPath: testErrorPath, recursive: true });
          console.log('rmdir', rmdir);
 
 
          let count = 0;
 
          const intervalId = setInterval(() => {
 
 
            const subPath = `${testErrorPath}/${count}`;
            createDir(subPath);
            fs.unzip({
              zipFilePath: path,
              targetPath: subPath,
              success: function (unzipResult) {
                console.log('解压成功', unzipResult); // 限制200mb大小,大概下载到70+的时候会触发100028的错误
 
                const fileList = fs.readdirSync({ dirPath: subPath });
                console.log('fileList', fileList);
                const unzipFileInfo = fs.getFileInfoSync({ filePath: subPath + '/' + fileList?.files?.[0] });
                console.log('unzipFileInfo', unzipFileInfo);
 
                // res(true);
              },
              fail: function (err) {
                console.log('解压失败', err);
                if(err?.error === '10028'){
                  console.warn('监控到10028错误,说明磁盘已满,清理可清理目录后可继续操作')
                  const rmdir = fs.rmdirSync({ dirPath: testErrorPath, recursive: true });
                  console.log('rmdir', rmdir);
                }else{
                  rej(false)
                }
              }
            });
 
            count++;
            if (count >= 200) {
              clearInterval(intervalId); // 停止定时器
              console.log('Completed 200 executions.');
            }
          }, 200); // 1000 毫秒(1秒)
 
        } catch (error) {
          console.log('解压前准备工作失败', error);
          rej(false)
        }
      },
      fail(res) {
        console.error('下载报错', res.errorMessage || res.error);
        rej(false);
      },
    })
  });
}
 
 
const downloadTest = async () => {
  console.log('本地缓存目录的虚拟路径', my.env.USER_DATA_PATH, { env: my.env });
 
  try {
    await readTextPromise(); // 读取文本文件示例
    await unzipPromise(); // 解压文件示例
    await testErrorCode10028(); // 内存满的监控和处理
 
 
  } catch (error) {
    console.error(error);
  }
}

调用

在game.js的代码尾部调用测试函数

1
downloadTest();

说明

  • 目前手机淘宝规定有2大类型文件缓存类型。临时文件缓存和用户文件缓存。临时文件缓存会定期清理,用户文件缓存则不会清理,需要开发者去维护存储空间,避免存储空间不足导致无法进行文件操作。
  • 目前临时文件缓存和用户文件缓存共享200mb的存储空间,开发者可以根据一下建议合理利用;
  • 缓存路径均已虚拟路径透出给开发者,开发者在使用接口传参的时候也是使用虚拟路径。临时文件缓存虚拟路径为:https://tmp/。用户文件缓存的虚拟路径为:https://usr/ ,可以用my.env.USER_DATA_PATH来获取。
  • 在IDE下,临时文件缓存虚拟路径为https://resource/,而非https://tmp/。

建议

建议1:在下载文件之前,对比已有文件的MD5码,避免重复下载。

  • 可以在服务器中维护一份需要下载的资源列表极其对应的MD5码。
  • 获取本地MD5码的参考getFileMD5函数。注意digestAlgorithm的值区分打小写;

建议2:将游戏的资源放在一个统一目录下,可以方便清理。

  • 删除文件夹使用的是rmdir接口,清理文件使用的是unlinke接口。
  • 创建文件夹和删除文件夹注意带上recursive: true参数;

建议3:监听到10028错误,则需要及时清空过期文件,否则无法进行后续操作。

  • 存储空间有限,需要手动维护空间,若不处理及时处理会导致存储空间超过200mb限制,文件操作会失败。
  • downloadFile、copyFile、writeFile和unzip会检查存储空间是否已满,如果分配的内存已满,会抛出10028的错误码;
  • 根据建议2,可以删除过期数据所在的文件夹,然后再对文件进行重试操作;

建议4:下载zip解压后,将zip包删除以节省空间。

  • 使用unlink接口进行文件删除;

FAQ

关于此文档暂时还没有FAQ
返回
顶部