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.INTERNET。 20> 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.INTERNET。 176 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