• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 应用文件上传下载
2<!--Kit: Basic Services Kit-->
3<!--Subsystem: Request-->
4<!--Owner: @huaxin05-->
5<!--Designer: @hu-kai45-->
6<!--Tester: @murphy1984-->
7<!--Adviser: @zhang_yixin13-->
8
9应用支持将文件上传到网络服务器,也支持从网络服务器下载资源文件到本地目录。
10
11## 上传应用文件
12
13开发者可以使用上传下载模块([ohos.request](../../reference/apis-basic-services-kit/js-apis-request.md))的上传接口将本地文件上传。文件上传过程通过系统服务代理完成。在api12中,`request.agent.create`接口增加了设置代理地址的参数,支持设置自定义代理地址。
14
15> **说明:**
16>
17> · 当前上传应用文件功能。request.uploadFile方式仅支持上传应用缓存文件路径(cacheDir)下的文件,request.agent方式支持上传用户公共文件和应用缓存文件路径下的文件。
18>
19> · 使用上传下载模块,需[声明权限](../../security/AccessToken/declare-permissions.md):ohos.permission.INTERNET20>
21> · 上传下载模块不支持Charles、Fiddler等代理抓包工具。
22>
23> · 上传下载模块接口目前暂不支持子线程调用场景,如[TaskPool](../../arkts-utils/taskpool-introduction.md)等。
24
25以下示例代码展示了两种将缓存文件上传至服务器的方法:
26
27```ts
28// 方式一:request.uploadFile
29// pages/xxx.ets
30import { common } from '@kit.AbilityKit';
31import fs from '@ohos.file.fs';
32import { BusinessError, request } from '@kit.BasicServicesKit';
33
34@Entry
35@Component
36struct Index {
37  build() {
38    Row() {
39      Column() {
40        Button("上传").onClick(() => {
41          // 获取应用文件路径
42          // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext
43          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
44          let cacheDir = context.cacheDir;
45
46          // 新建一个本地应用文件
47          try {
48            let file = fs.openSync(cacheDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
49            fs.writeSync(file.fd, 'upload file test');
50            fs.closeSync(file);
51          } catch (error) {
52            let err: BusinessError = error as BusinessError;
53            console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
54          }
55
56          // 上传任务配置项
57          let files: Array<request.File> = [
58          //uri前缀internal://cache 对应cacheDir目录
59            { filename: 'test.txt', name: 'test', uri: 'internal://cache/test.txt', type: 'txt' }
60          ]
61          let data: Array<request.RequestData> = [{ name: 'name', value: 'value' }];
62          let uploadConfig: request.UploadConfig = {
63            url: 'https://xxx',
64            header: {
65              'key1':'value1',
66              'key2':'value2'
67            },
68            method: 'POST',
69            files: files,
70            data: data
71          }
72
73          // 将本地应用文件上传至网络服务器
74          try {
75            request.uploadFile(context, uploadConfig)
76              .then((uploadTask: request.UploadTask) => {
77                uploadTask.on('complete', (taskStates: Array<request.TaskState>) => {
78                  for (let i = 0; i < taskStates.length; i++) {
79                    console.info(`upload complete taskState: ${JSON.stringify(taskStates[i])}`);
80                  }
81                });
82              })
83              .catch((err: BusinessError) => {
84                console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
85              })
86          } catch (error) {
87            let err: BusinessError = error as BusinessError;
88            console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
89          }
90        })
91      }
92    }
93  }
94}
95```
96
97```ts
98// 方式二:request.agent
99// pages/xxx.ets
100import { common } from '@kit.AbilityKit';
101import fs from '@ohos.file.fs';
102import { BusinessError, request } from '@kit.BasicServicesKit';
103
104@Entry
105@Component
106struct Index {
107  build() {
108    Row() {
109      Column() {
110        Button("上传").onClick(() => {
111          // 获取应用文件路径
112          // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext
113          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
114          let cacheDir = context.cacheDir;
115
116          // 新建一个本地应用文件
117          let file = fs.openSync(cacheDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
118          fs.writeSync(file.fd, 'upload file test');
119          fs.closeSync(file);
120          let attachments: Array<request.agent.FormItem> = [{
121            name: "test",
122            value: [
123              {
124                filename: "test.txt",
125                path: cacheDir + '/test.txt',
126              },
127            ]
128          }];
129          let config: request.agent.Config = {
130            action: request.agent.Action.UPLOAD,
131            url: 'http://xxx',
132            mode: request.agent.Mode.FOREGROUND,
133            overwrite: true,
134            method: "POST",
135            headers: {
136              'key1':'value1',
137              'key2':'value2'
138            },
139            data: attachments
140          };
141          request.agent.create(context, config).then((task: request.agent.Task) => {
142            task.start((err: BusinessError) => {
143              if (err) {
144                console.error(`Failed to start the upload task, Code: ${err.code}  message: ${err.message}`);
145                return;
146              }
147            });
148            task.on('progress', async(progress) => {
149              console.warn(`/Request upload status ${progress.state}, uploaded ${progress.processed}`);
150            })
151            task.on('completed', async() => {
152              console.warn(`/Request upload completed`);
153              //该方法需用户管理任务生命周期,任务结束后调用remove释放task对象
154              request.agent.remove(task.tid);
155            })
156          }).catch((err: BusinessError) => {
157            console.error(`Failed to create a upload task, Code: ${err.code}, message: ${err.message}`);
158          });
159        })
160      }
161    }
162  }
163}
164
165```
166
167## 下载网络资源文件至应用文件目录
168
169开发者可以使用上传下载模块([ohos.request](../../reference/apis-basic-services-kit/js-apis-request.md))的下载接口将网络资源文件下载到应用文件目录。对已下载的网络资源应用文件,开发者可以使用基础文件IO接口([ohos.file.fs](../../reference/apis-core-file-kit/js-apis-file-fs.md))对其进行访问,使用方式与[应用文件访问](../../file-management/app-file-access.md)一致。文件下载过程使用系统服务代理完成,在api12中request.agent.create接口增加了设置代理地址参数,支持用户设置自定义代理地址。
170
171> **说明:**
172>
173> 当前网络资源文件仅支持下载至应用文件目录。
174>
175> 使用上传下载模块,需[声明权限](../../security/AccessToken/declare-permissions.md):ohos.permission.INTERNET176
177以下示例代码展示了将网络资源文件下载到应用内部文件目录的两种方法:
178
179```ts
180// 方式一:request.downloadFile
181// pages/xxx.ets
182// 将网络资源文件下载到应用文件目录并读取一段内容
183import { common } from '@kit.AbilityKit';
184import fs from '@ohos.file.fs';
185import { BusinessError, request } from '@kit.BasicServicesKit';
186import { buffer } from '@kit.ArkTS';
187
188@Entry
189@Component
190struct Index {
191  build() {
192    Row() {
193      Column() {
194        Button("下载").onClick(() => {
195          // 获取应用文件路径
196          // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext
197          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
198          let filesDir = context.filesDir;
199
200          try {
201            request.downloadFile(context, {
202              url: 'https://xxxx/xxxx.txt',
203              filePath: filesDir + '/xxxx.txt'
204            }).then((downloadTask: request.DownloadTask) => {
205              downloadTask.on('complete', () => {
206                console.info('download complete');
207                let file = fs.openSync(filesDir + '/xxxx.txt', fs.OpenMode.READ_WRITE);
208                let arrayBuffer = new ArrayBuffer(1024);
209                let readLen = fs.readSync(file.fd, arrayBuffer);
210                let buf = buffer.from(arrayBuffer, 0, readLen);
211                console.info(`The content of file: ${buf.toString()}`);
212                fs.closeSync(file);
213              })
214            }).catch((err: BusinessError) => {
215              console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
216            });
217          } catch (error) {
218            let err: BusinessError = error as BusinessError;
219            console.error(`Invoke downloadFile failed, code is ${err.code}, message is ${err.message}`);
220          }
221        })
222      }
223    }
224  }
225}
226```
227```ts
228// 方式二:request.agent
229// pages/xxx.ets
230// 将网络资源文件下载到应用文件目录并读取一段内容
231import { common } from '@kit.AbilityKit';
232import fs from '@ohos.file.fs';
233import { BusinessError, request } from '@kit.BasicServicesKit';
234import { buffer } from '@kit.ArkTS';
235
236@Entry
237@Component
238struct Index {
239  build() {
240    Row() {
241      Column() {
242        Button("下载").onClick(() => {
243          // 获取应用文件路径
244          // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext
245          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
246          let filesDir = context.filesDir;
247
248          let config: request.agent.Config = {
249            action: request.agent.Action.DOWNLOAD,
250            url: 'https://xxxx/test.txt',
251            saveas: 'xxxx.txt',
252            gauge: true,
253            overwrite: true,
254            network: request.agent.Network.WIFI,
255          };
256          request.agent.create(context, config).then((task: request.agent.Task) => {
257            task.start((err: BusinessError) => {
258              if (err) {
259                console.error(`Failed to start the download task, Code: ${err.code}  message: ${err.message}`);
260                return;
261              }
262            });
263            task.on('progress', async (progress) => {
264              console.warn(`/Request download status ${progress.state}, downloaded ${progress.processed}`);
265            })
266            task.on('completed', async () => {
267              console.warn(`/Request download completed`);
268              let filePath = filesDir + '/xxxx.txt';
269              let file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY); // 先用只读模式打开获取大小
270
271              // 获取文件状态信息,其中包含大小
272              let fileStat = fileIo.statSync(filePath);
273              let fileSize = fileStat.size;
274
275              // 根据文件大小创建足够大的Buffer
276              let arrayBuffer = new ArrayBuffer(fileSize);
277              let readLen = fileIo.readSync(file.fd, arrayBuffer); // 现在可以安全读取全部内容
278              let buf = buffer.from(arrayBuffer, 0, readLen);
279              console.info(`The content of file: ${buf.toString()}`);
280              fileIo.closeSync(file);
281              request.agent.remove(task.tid);
282            })
283          }).catch((err: BusinessError) => {
284            console.error(`Failed to create a download task, Code: ${err.code}, message: ${err.message}`);
285          });
286        })
287      }
288    }
289  }
290}
291
292```
293
294## 下载网络资源文件至用户文件
295开发者可以使用[ohos.request](../../reference/apis-basic-services-kit/js-apis-request.md)的[request.agent](../../reference/apis-basic-services-kit/js-apis-request.md#requestagentcreate10)接口下载网络资源文件到指定的用户文件目录。
296
297> **说明:**
298>
299> 从API version 20开始支持下载网络资源文件至用户文件。
300
301### 下载文档类文件
302
303开发者可以通过调用[DocumentViewPicker](../../reference/apis-core-file-kit/js-apis-file-picker.md#documentviewpicker)的[save()](../../reference/apis-core-file-kit/js-apis-file-picker.md#save)接口保存文件并获得用户文件的uri,将此uri作为[Config](../../reference/apis-basic-services-kit/js-apis-request.md#config10)的saveas字段值进行下载。
304
305```ts
306import { BusinessError, request } from '@kit.BasicServicesKit';
307import { picker } from '@kit.CoreFileKit';
308import { common } from '@kit.AbilityKit';
309
310@Entry
311@Component
312struct Index {
313  build() {
314    Row() {
315      Column() {
316        Button("下载文档").width("50%").margin({ top: 20 }).height(40).onClick(async () => {
317
318          // 创建文件管理器选项实例。
319          try {
320            const documentSaveOptions = new picker.DocumentSaveOptions();
321            // 保存文件名(可选)。 默认为空。
322            documentSaveOptions.newFileNames = ["xxxx.txt"];
323            // 保存文件类型['后缀类型描述|后缀类型'],选择所有文件:'所有文件(*.*)|.*'(可选),如果选择项存在多个后缀(最大限制100个过滤后缀),默认选择第一个。如果不传该参数,默认无过滤后缀。
324            documentSaveOptions.fileSuffixChoices = ['文档|.txt', '.pdf'];
325            let uri: string = '';
326            // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext
327            let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
328            const documentViewPicker = new picker.DocumentViewPicker(context);
329            await documentViewPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {
330              uri = documentSaveResult[0];
331              console.info('DocumentViewPicker.save to file succeed and uri is:' + uri);
332            }).catch((err: BusinessError) => {
333              console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
334            })
335            if (uri != '') {
336              let config: request.agent.Config = {
337                action: request.agent.Action.DOWNLOAD,
338                url: 'https://xxxx/xxxx.txt',
339                // saveas字段是DocumentViewPicker保存的文件的uri
340                saveas: uri,
341                gauge: true,
342                // overwrite字段必须为true
343                overwrite: true,
344                network: request.agent.Network.WIFI,
345                // mode字段必须为request.agent.Mode.FOREGROUND
346                mode: request.agent.Mode.FOREGROUND,
347              };
348              try {
349                request.agent.create(context, config).then((task: request.agent.Task) => {
350                  task.start((err: BusinessError) => {
351                    if (err) {
352                      console.error(`Failed to start the download task, Code: ${err.code}  message: ${err.message}`);
353                      return;
354                    }
355                  });
356                  task.on('progress', async (progress) => {
357                    console.warn(`Request download status ${progress.state}, downloaded ${progress.processed}`);
358                  })
359                  task.on('completed', async (progress) => {
360                    console.warn('Request download completed, ' + JSON.stringify(progress));
361                    // 该方法需用户管理任务生命周期,任务结束后调用remove释放task对象
362                    request.agent.remove(task.tid);
363                  })
364                }).catch((err: BusinessError) => {
365                  console.error(`Failed to operate a download task, Code: ${err.code}, message: ${err.message}`);
366                });
367              } catch (err) {
368                console.error(`Failed to create a download task, err: ${err}`);
369              }
370            }
371          } catch (err) {
372            console.error(`Failed to create a documentSaveOptions, err: ${err}`);
373            return;
374          }
375        })
376      }
377    }
378  }
379}
380```
381
382### 下载音频类文件
383
384开发者可以通过调用[AudioViewPicker](../../reference/apis-core-file-kit/js-apis-file-picker.md#audioviewpicker)的[save()](../../reference/apis-core-file-kit/js-apis-file-picker.md#save-3)接口保存文件并获得用户文件的uri,将此uri作为[Config](../../reference/apis-basic-services-kit/js-apis-request.md#config10)的saveas字段值进行下载。
385
386```ts
387import { BusinessError, request } from '@kit.BasicServicesKit';
388import { picker } from '@kit.CoreFileKit';
389import { common } from '@kit.AbilityKit';
390
391@Entry
392@Component
393struct Index {
394  build() {
395    Row() {
396      Column() {
397        Button("下载音频").width("50%").margin({ top: 20 }).height(40).onClick(async () => {
398          // 创建文件管理器选项实例。
399          const audioSaveOptions = new picker.AudioSaveOptions();
400          // 保存文件名(可选)。 默认为空。
401          audioSaveOptions.newFileNames = ['xxxx.mp3'];
402
403          let uri: string = '';
404          // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext
405          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
406          const audioViewPicker = new picker.AudioViewPicker(context);
407          await audioViewPicker.save(audioSaveOptions).then((audioSelectResult: Array<string>) => {
408            uri = audioSelectResult[0];
409            console.info('AudioViewPicker.save to file succeed and uri is:' + uri);
410          }).catch((err: BusinessError) => {
411            console.error(`Invoke audioViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
412          })
413          if (uri != '') {
414            let config: request.agent.Config = {
415              action: request.agent.Action.DOWNLOAD,
416              url: 'https://xxxx/xxxx.mp3',
417              // saveas字段是AudioViewPicker保存的文件的uri
418              saveas: uri,
419              gauge: true,
420              // overwrite字段必须为true
421              overwrite: true,
422              network: request.agent.Network.WIFI,
423              // mode字段必须为request.agent.Mode.FOREGROUND
424              mode: request.agent.Mode.FOREGROUND,
425            };
426            try {
427              request.agent.create(context, config).then((task: request.agent.Task) => {
428                task.start((err: BusinessError) => {
429                  if (err) {
430                    console.error(`Failed to start the download task, Code: ${err.code}  message: ${err.message}`);
431                    return;
432                  }
433                });
434                task.on('progress', async (progress) => {
435                  console.warn(`Request download status ${progress.state}, downloaded ${progress.processed}`);
436                })
437                task.on('completed', async (progress) => {
438                  console.warn('Request download completed, ' + JSON.stringify(progress));
439                  // 该方法需用户管理任务生命周期,任务结束后调用remove释放task对象
440                  request.agent.remove(task.tid);
441                })
442              }).catch((err: BusinessError) => {
443                console.error(`Failed to operate a download task, Code: ${err.code}, message: ${err.message}`);
444              });
445            } catch (err) {
446              console.error(`Failed to create a download task, err: ${err}`);
447            }
448          }
449        })
450      }
451    }
452  }
453}
454```
455
456### 下载图片或视频类文件
457
458开发者可以通过调用[PhotoAccessHelper](../../reference/apis-media-library-kit/arkts-apis-photoAccessHelper.md)的[createAsset()](../../reference/apis-media-library-kit/arkts-apis-photoAccessHelper-PhotoAccessHelper.md#createasset-2)接口创建媒体文件并获取用户文件的URI,将其作为[Config](../../reference/apis-basic-services-kit/js-apis-request.md#config10)的saveas字段值进行下载。
459
460需要权限:[ohos.permission.WRITE_IMAGEVIDEO](../../security/AccessToken/permissions-for-all-user.md#ohospermissionwrite_media)
461
462权限[ohos.permission.WRITE_IMAGEVIDEO](../../security/AccessToken/permissions-for-all-user.md#ohospermissionwrite_media)是[system_basic](../../security/AccessToken/app-permission-mgmt-overview.md#权限机制中的基本概念)级别的[受限开放权限](../../security/AccessToken/restricted-permissions.md),normal等级的应用需要将自身的APL等级声明为system_basic及以上。授权方式为user_grant,需要[向用户申请授权](../../security/AccessToken/request-user-authorization.md)。
463
464```ts
465import { BusinessError, request } from '@kit.BasicServicesKit';
466import { photoAccessHelper } from '@kit.MediaLibraryKit';
467
468import { bundleManager } from '@kit.AbilityKit';
469import { abilityAccessCtrl, Context, PermissionRequestResult, common } from '@kit.AbilityKit';
470
471@Entry
472@Component
473struct Index {
474  build() {
475    Row() {
476      Column() {
477        Button("下载图片").width("50%").margin({ top: 20 }).height(40).onClick(async () => {
478          let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION |
479          bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_METADATA;
480          // 获取应用程序的accessTokenID。
481          let tokenID = -1;
482          try {
483            await bundleManager.getBundleInfoForSelf(bundleFlags).then((data) => {
484              console.info(`/Request getBundleInfoForSelf successfully. Data: ${JSON.stringify(data)}`);
485              tokenID = data.appInfo.accessTokenId;
486            }).catch((err: BusinessError) => {
487              console.error(`GetBundleInfoForSelf failed. Cause: ${err.message}`);
488            });
489          } catch (err) {
490            let message = (err as BusinessError).message;
491            console.error('GetBundleInfoForSelf failed: %{public}s', message);
492          }
493          let context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;
494
495          let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
496          let grant = true;
497          // 校验应用是否授予权限。使用Promise异步回调。
498          await atManager.checkAccessToken(tokenID, 'ohos.permission.WRITE_IMAGEVIDEO')
499            .then((data: abilityAccessCtrl.GrantStatus) => {
500              console.log(`/Request checkAccessToken success, data->${JSON.stringify(data)}`);
501              if (data != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
502                grant = false;
503              }
504            })
505            .catch((err: BusinessError) => {
506              console.error(`CheckAccessToken fail, err->${JSON.stringify(err)}`);
507            });
508
509          if (!grant) {
510            // 用于UIAbility拉起弹框请求用户授权。使用callback异步回调。
511            await atManager.requestPermissionsFromUser(context, ['ohos.permission.WRITE_IMAGEVIDEO'])
512              .then((data: PermissionRequestResult) => {
513                console.info('/Request grant:' + JSON.stringify(data));
514                console.info('/Request grant permissions:' + data.permissions);
515                console.info('/Request grant authResults:' + data.authResults);
516                console.info('/Request grant dialogShownResults:' + data.dialogShownResults);
517              }).catch((err: BusinessError) => {
518                console.error('Grant error:' + JSON.stringify(err));
519              });
520          }
521
522          try {
523            let photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE;
524            let extension: string = 'jpg';
525            let options: photoAccessHelper.CreateOptions = {
526              title: 'xxxx'
527            }
528            // 获取相册管理模块的实例,用于访问和修改相册中的媒体文件。
529            let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
530            // 指定文件类型、后缀和创建选项,创建图片或视频资源,以Promise方式返回结果。
531            let uri: string = await phAccessHelper.createAsset(photoType, extension, options);
532            console.info('/Request createAsset uri' + uri);
533            console.info('/Request createAsset successfully');
534
535            let config: request.agent.Config = {
536              action: request.agent.Action.DOWNLOAD,
537              url: 'https://xxxx/xxxx.jpg',
538              // saveas字段是PhotoAccessHelper保存的文件的uri
539              saveas: uri,
540              gauge: true,
541              // overwrite字段必须为true
542              overwrite: true,
543              network: request.agent.Network.WIFI,
544              // mode字段必须为request.agent.Mode.FOREGROUND
545              mode: request.agent.Mode.FOREGROUND,
546            };
547            request.agent.create(context, config).then((task: request.agent.Task) => {
548              task.start((err: BusinessError) => {
549                if (err) {
550                  console.error(`Failed to start the download task, Code: ${err.code}  message: ${err.message}`);
551                  return;
552                }
553              });
554              task.on('progress', async (progress) => {
555                console.warn(`Request download status ${progress.state}, downloaded ${progress.processed}`);
556              })
557              task.on('completed', async (progress) => {
558                console.warn('Request download completed, ' + JSON.stringify(progress));
559                //该方法需用户管理任务生命周期,任务结束后调用remove释放task对象
560                request.agent.remove(task.tid);
561              })
562            }).catch((err: BusinessError) => {
563              console.error(`Failed to operate a download task, Code: ${err.code}, message: ${err.message}`);
564            });
565          } catch (err) {
566            console.error(`Failed to create a download task, err: ${err}`);
567          }
568        })
569      }
570    }
571  }
572}
573```
574
575## 添加任务速度限制与超时限制
576
577开发者可以使用[ohos.request (上传下载)](../../reference/apis-basic-services-kit/js-apis-request.md)模块中的接口上传本地文件或下载网络资源文件。为方便对任务速度及时长进行限制,分别在API version 18中增加了[setMaxSpeed](../../reference/apis-basic-services-kit/js-apis-request.md#setmaxspeed18)接口,支持用户设置任务的最高速度限制;在API version 20的[request.agent.create](../../reference/apis-basic-services-kit/js-apis-request.md#requestagentcreate10-1)接口中增加了最低限速及超时参数,支持用户自定义最低速度限制以及超时时间。
578
579以下是对下载任务进行速度限制与超时限制的方式的示例代码演示:
580
581```ts
582// pages/xxx.ets
583// 将网络资源文件下载到应用文件目录并读取一段内容
584import { common } from '@kit.AbilityKit';
585import { fileIo } from '@kit.CoreFileKit';
586import { BusinessError, request } from '@kit.BasicServicesKit';
587import { buffer } from '@kit.ArkTS';
588
589@Entry
590@Component
591struct Index {
592    build() {
593        Row() {
594            Column() {
595                Button("下载").onClick(() => {
596                    // 获取应用文件路径
597                    // 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext
598                    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
599                    let filesDir = context.filesDir;
600
601                    let config: request.agent.Config = {
602                        action: request.agent.Action.DOWNLOAD,
603                        url: 'https://xxxx/test.txt',
604                        saveas: 'xxxx.txt',
605                        gauge: true,
606                        overwrite: true,
607                        network: request.agent.Network.WIFI,
608                        // 最低速度限制规则:
609                        // 1. 若任务速度持续低于设定值(如:16 * 1024 B/s)达到指定时长(如:10s),则任务失败
610                        // 2. 重置计时条件:
611                        //    - 任一秒速度超过最低限速
612                        //    - 任务暂停后恢复
613                        //    - 任务停止后重启
614                        minSpeed: {
615                            speed: 16 * 1024,
616                            duration: 10
617                        },
618                        // 超时控制规则:
619                        // 1. 连接超时(connectionTimeout):
620                        //    - 单次连接建立耗时超过设定值(如:60s)则任务失败
621                        //    - 多次连接时各次独立计时(不累积)
622                        // 2. 总超时(totalTimeout):
623                        //    - 任务总耗时(含连接+传输时间)超过设定值(如:120s)则失败
624                        //    - 暂停期间不计时,恢复后累积计时
625                        // 3. 重置计时条件:任务失败或停止时重置计时
626                        timeout: {
627                            connectionTimeout: 60,
628                            totalTimeout: 120,
629                        }
630                    };
631                    request.agent.create(context, config).then((task: request.agent.Task) => {
632                        // 设置任务速度上限
633                        task.setMaxSpeed(10 * 1024 * 1024).then(() => {
634                            console.info(`Succeeded in setting the max speed of the task. result: ${task.tid}`);
635                        }).catch((err: BusinessError) => {
636                            console.error(`Failed to set the max speed of the task. result: ${task.tid}`);
637                        });
638                        task.start((err: BusinessError) => {
639                            if (err) {
640                                console.error(`Failed to start the download task, Code: ${err.code}  message: ${err.message}`);
641                                return;
642                            }
643                        });
644                        task.on('progress', async (progress) => {
645                            console.warn(`/Request download status ${progress.state}, downloaded ${progress.processed}`);
646                        })
647                        task.on('completed', async () => {
648                            console.warn(`/Request download completed`);
649                            let file = fileIo.openSync(filesDir + '/xxxx.txt', fileIo.OpenMode.READ_WRITE);
650                            let arrayBuffer = new ArrayBuffer(1024);
651                            let readLen = fileIo.readSync(file.fd, arrayBuffer);
652                            let buf = buffer.from(arrayBuffer, 0, readLen);
653                            console.info(`The content of file: ${buf.toString()}`);
654                            fileIo.closeSync(file);
655                            request.agent.remove(task.tid);
656                        })
657                    }).catch((err: BusinessError) => {
658                        console.error(`Failed to create a download task, Code: ${err.code}, message: ${err.message}`);
659                    });
660                })
661            }
662        }
663    }
664}
665```
666
667## 添加网络配置
668
669### HTTP拦截
670
671开发者可以通过设置配置文件实现HTTP拦截功能。上传下载模块在应用配置文件中禁用HTTP后,无法创建明文HTTP传输的上传下载任务。配置文件在APP中的路径是:`src/main/resources/base/profile/network_config.json`。请参考网络管理模块[配置文件](../../reference/apis-network-kit/js-apis-net-connection.md#connectionsetapphttpproxy11),了解需要配置的具体参数。
672
673参考配置文件如下所示:
674
675```ts
676{
677  "network-security-config": {
678    "base-config": {
679      "cleartextTrafficPermitted": true,
680      "trust-anchors": [
681        {
682          "certificates": "/etc/security/certificates"
683        }
684      ]
685    },
686    "domain-config": [
687      {
688        "cleartextTrafficPermitted": true,
689        "domains": [
690          {
691            "include-subdomains": true,
692            "name": "*.example.com"
693          }
694        ],
695      }
696    ]
697  }
698}
699```
700
701