• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Downloading Files
2
3To download files on the web page, you can use the following method to invoke the web APIs.
4
5## Listening for Downloads Initiated from Pages
6
7Call [setDownloadDelegate()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#setdownloaddelegate11) to register a **DownloadDelegate** object with the **Web** component to listen for downloads initiated from pages. While the **Web** component downloads resources as requested, it notifies the application of the download progress through the **DownloadDelegate** object.
8
9In the following example, the **index.html** and **download.html** files are added to the **rawfile** folder of the application. After the application is started, a **Web** component is created and the **index.html** file is loaded. After **setDownloadDelegate** is clicked, a **DownloadDelegate** object is registered with the **Web** component. This **DownloadDelegate** object listens for any downloads initiated by clicking the download button on the page.
10
11```ts
12// xxx.ets
13import { webview } from '@kit.ArkWeb';
14import { BusinessError } from '@kit.BasicServicesKit';
15
16@Entry
17@Component
18struct WebComponent {
19  controller: webview.WebviewController = new webview.WebviewController();
20  delegate: webview.WebDownloadDelegate = new webview.WebDownloadDelegate();
21
22  build() {
23    Column() {
24      Button('setDownloadDelegate')
25        .onClick(() => {
26          try {
27            this.delegate.onBeforeDownload((webDownloadItem: webview.WebDownloadItem) => {
28              console.log("will start a download.");
29              // Pass in a download path and start the download.
30              // If the path is invalid, the file will be downloaded to the default directory at /data/storage/el2/base/cache/web/.
31              webDownloadItem.start("/data/storage/el2/base/cache/web/" + webDownloadItem.getSuggestedFileName());
32            })
33            this.delegate.onDownloadUpdated((webDownloadItem: webview.WebDownloadItem) => {
34              // Unique ID of a download task.
35              console.log("download update guid: " + webDownloadItem.getGuid());
36              // Download progress.
37              console.log("download update percent complete: " + webDownloadItem.getPercentComplete());
38              // Current download speed.
39              console.log("download update speed: " + webDownloadItem.getCurrentSpeed())
40            })
41            this.delegate.onDownloadFailed((webDownloadItem: webview.WebDownloadItem) => {
42              console.error("download failed guid: " + webDownloadItem.getGuid());
43              // Error code of a download task failure.
44              console.error("download failed last error code: " + webDownloadItem.getLastErrorCode());
45            })
46            this.delegate.onDownloadFinish((webDownloadItem: webview.WebDownloadItem) => {
47              console.log("download finish guid: " + webDownloadItem.getGuid());
48            })
49            this.controller.setDownloadDelegate(this.delegate);
50          } catch (error) {
51            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
52          }
53        })
54      Web({ src: $rawfile('index.html'), controller: this.controller })
55    }
56  }
57}
58```
59
60HTML file to be loaded:
61```html
62<!-- index.html -->
63<!DOCTYPE html>
64<html>
65<body>
66// Click the download button in the lower right corner of the video to trigger a download task.
67<video controls="controls" width="800px" height="580px"
68       src="http://vjs.zencdn.net/v/oceans.mp4"
69       type="video/mp4">
70</video>
71<a href='data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E' download='download.html'>Download the download.html</a>
72</body>
73</html>
74```
75
76HTML file to be downloaded:
77```html
78<!-- download.html -->
79<!DOCTYPE html>
80<html>
81<body>
82<h1>download test</h1>
83</body>
84</html>
85```
86
87## Initiating a Download Task
88
89Call [startDownload()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#startdownload11) to initiate a download task.
90For a download initiated by it, the **Web** component works out the referrer based on the currently displayed URL and its own default referrer policy.
91
92  In the following example, clicking **setDownloadDelegate** registers a listener class with the **Web** component, and clicking **startDownload** initiates a download task.
93  The application is notified of the download task progress through the configured **DownloadDelegate** object.
94
95```ts
96// xxx.ets
97import { webview } from '@kit.ArkWeb';
98import { BusinessError } from '@kit.BasicServicesKit';
99
100@Entry
101@Component
102struct WebComponent {
103  controller: webview.WebviewController = new webview.WebviewController();
104  delegate: webview.WebDownloadDelegate = new webview.WebDownloadDelegate();
105
106  build() {
107    Column() {
108      Button('setDownloadDelegate')
109        .onClick(() => {
110          try {
111            this.delegate.onBeforeDownload((webDownloadItem: webview.WebDownloadItem) => {
112              console.log("will start a download.");
113              // Pass in a download path and start the download.
114              webDownloadItem.start("/data/storage/el2/base/cache/web/" + webDownloadItem.getSuggestedFileName());
115            })
116            this.delegate.onDownloadUpdated((webDownloadItem: webview.WebDownloadItem) => {
117              console.log("download update guid: " + webDownloadItem.getGuid());
118            })
119            this.delegate.onDownloadFailed((webDownloadItem: webview.WebDownloadItem) => {
120              console.error("download failed guid: " + webDownloadItem.getGuid());
121            })
122            this.delegate.onDownloadFinish((webDownloadItem: webview.WebDownloadItem) => {
123              console.log("download finish guid: " + webDownloadItem.getGuid());
124            })
125            this.controller.setDownloadDelegate(this.delegate);
126          } catch (error) {
127            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
128          }
129        })
130      Button('startDownload')
131        .onClick(() => {
132          try {
133            // The specified download address here is https://www.example.com/.
134            // Replace it with the URL from which you want to download files.
135            this.controller.startDownload('https://www.example.com/');
136          } catch (error) {
137            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
138          }
139        })
140      Web({ src: 'www.example.com', controller: this.controller })
141    }
142  }
143}
144```
145
146Use [DocumentViewPicker()](../reference/apis-core-file-kit/js-apis-file-picker.md#documentviewpicker) to obtain the default download directory and set it as the download directory.
147```ts
148// xxx.ets
149import { webview } from '@kit.ArkWeb';
150import { BusinessError } from '@kit.BasicServicesKit';
151import { picker, fileUri } from  '@kit.CoreFileKit';
152@Entry
153@Component
154struct WebComponent {
155  controller: webview.WebviewController = new webview.WebviewController();
156  delegate: webview.WebDownloadDelegate = new webview.WebDownloadDelegate();
157
158  build() {
159    Column() {
160      Button('setDownloadDelegate')
161        .onClick(() => {
162          try {
163            this.delegate.onBeforeDownload((webDownloadItem: webview.WebDownloadItem) => {
164              console.log("will start a download.");
165              // Use DocumentViewPicker() to obtain the default download directory and set it as the download directory.
166              getDownloadPathFromPicker().then((downloadPath) => {
167                webDownloadItem.start(downloadPath + '/' + webDownloadItem.getSuggestedFileName());
168              });
169
170            })
171            this.delegate.onDownloadUpdated((webDownloadItem: webview.WebDownloadItem) => {
172              // Unique ID of a download task.
173              console.log("download update guid: " + webDownloadItem.getGuid());
174              // Download progress.
175              console.log("download update percent complete: " + webDownloadItem.getPercentComplete());
176              // Current download speed.
177              console.log("download update speed: " + webDownloadItem.getCurrentSpeed())
178            })
179            this.delegate.onDownloadFailed((webDownloadItem: webview.WebDownloadItem) => {
180              console.error("download failed guid: " + webDownloadItem.getGuid());
181              // Error code of a download task failure.
182              console.error("download failed last error code: " + webDownloadItem.getLastErrorCode());
183            })
184            this.delegate.onDownloadFinish((webDownloadItem: webview.WebDownloadItem) => {
185              console.log("download finish guid: " + webDownloadItem.getGuid());
186            })
187            this.controller.setDownloadDelegate(this.delegate);
188          } catch (error) {
189            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
190          }
191        })
192      Web({ src: $rawfile('index.html'), controller: this.controller })
193    }
194  }
195
196}
197function getDownloadPathFromPicker(): Promise<string> {
198  return new Promise<string>(resolve => {
199    try {
200      const documentSaveOptions = new picker.DocumentSaveOptions();
201      documentSaveOptions.pickerMode = picker.DocumentPickerMode.DOWNLOAD
202      const documentPicker = new picker.DocumentViewPicker();
203      documentPicker.save(documentSaveOptions).then(async (documentSaveResult: Array<string>) => {
204        if (documentSaveResult.length <= 0) {
205          resolve('');
206          return;
207        }
208        const uriString = documentSaveResult[0];
209        if (!uriString) {
210          resolve('');
211          return;
212        }
213        const uri = new fileUri.FileUri(uriString);
214        resolve(uri.path);
215      }).catch((err: BusinessError) => {
216        console.error(`ErrorCode: ${err.code},  Message: ${err.message}`);
217        resolve('');
218      });
219    } catch (error) {
220      resolve('');
221    }
222  })
223}
224```
225
226> **NOTE**
227>
228>Call [WebDownloadItem.start](../reference/apis-arkweb/arkts-apis-webview-WebDownloadItem.md#start11) to specify the path for storing downloaded files.
229>
230>Note that **WebDownloadItem.start** does not start the download. The download process starts when the user clicks the page link. **WebDownloadItem.start** is used to move the content that has been downloaded to the temporary directory (**/data/storage/el2/base/cache/web/Temp/**) to the specified path, and directly save the remaining content to the specified path. Call[WebDownloadItem.cancel](../reference/apis-arkweb/arkts-apis-webview-WebDownloadItem.md#cancel11) to cancel the current download task. In this case, the temporary file is deleted.
231>
232>If you do not want to download the file to the temporary directory before **WebDownloadItem.start**, you can also use **WebDownloadItem.cancel** to interrupt the download. In addition, the interrupted download can be resumed using [WebDownloadManager.resumeDownload](../reference/apis-arkweb/arkts-apis-webview-WebDownloadManager.md#resumedownload11).
233
234## Resuming Unfinished Download Tasks Due to Process Exit
235When the **Web** component is started, you can resume the unfinished download task through the [resumeDownload()](../reference/apis-arkweb/arkts-apis-webview-WebDownloadManager.md#resumedownload11) API.
236
237In the following example, the **record** button is used to save the current download task to a persistent file. After the application is restarted, the **recovery** button can be used to resume the persistent download task. If multiple download tasks need to be saved, the application can adjust the persistence time and mode as required.
238```ts
239// xxx.ets
240import { webview } from '@kit.ArkWeb';
241import { BusinessError } from '@kit.BasicServicesKit';
242import { downloadUtil, fileName, filePath } from './downloadUtil'; // downloadUtil.ets is described below.
243
244@Entry
245@Component
246struct WebComponent {
247  controller: webview.WebviewController = new webview.WebviewController();
248  delegate: webview.WebDownloadDelegate = new webview.WebDownloadDelegate();
249  download: webview.WebDownloadItem = new webview.WebDownloadItem();
250  // Used to record failed download tasks.
251  failedData: Uint8Array = new Uint8Array();
252
253  aboutToAppear(): void {
254    downloadUtil.init(this.getUIContext());
255  }
256
257  build() {
258    Column() {
259      Button('setDownloadDelegate')
260        .onClick(() => {
261          try {
262            this.delegate.onBeforeDownload((webDownloadItem: webview.WebDownloadItem) => {
263              console.log("will start a download.");
264              // Pass in a download path and start the download.
265              webDownloadItem.start("/data/storage/el2/base/cache/web/" + webDownloadItem.getSuggestedFileName());
266            })
267            this.delegate.onDownloadUpdated((webDownloadItem: webview.WebDownloadItem) => {
268              console.log("download update percent complete: " + webDownloadItem.getPercentComplete());
269              this.download = webDownloadItem;
270            })
271            this.delegate.onDownloadFailed((webDownloadItem: webview.WebDownloadItem) => {
272              console.error("download failed guid: " + webDownloadItem.getGuid());
273              // Serialize the failed download task to a byte array.
274              this.failedData = webDownloadItem.serialize();
275            })
276            this.delegate.onDownloadFinish((webDownloadItem: webview.WebDownloadItem) => {
277              console.log("download finish guid: " + webDownloadItem.getGuid());
278            })
279            this.controller.setDownloadDelegate(this.delegate);
280            webview.WebDownloadManager.setDownloadDelegate(this.delegate);
281          } catch (error) {
282            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
283          }
284        })
285      Button('startDownload')
286        .onClick(() => {
287          try {
288            // The specified download address here is https://www.example.com/.
289            // Replace it with the URL from which you want to download files.
290            this.controller.startDownload('https://www.example.com/');
291          } catch (error) {
292            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
293          }
294        })
295      // Serialize and save the current download task information for subsequent download task resumption.
296      // This example shows the one-task download scenario. For multiple download tasks, extend the code as required.
297      Button('record')
298        .onClick(() => {
299          try {
300            // Save the downloaded data to a persistent file.
301            downloadUtil.saveDownloadInfo(downloadUtil.uint8ArrayToStr(this.download.serialize()));
302          } catch (error) {
303            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
304          }
305        })
306      // Resume the download task from the serialized download task information.
307      // Ensure that the WebDownloadManager.setDownloadDelegate setting is complete when the button is triggered.
308      Button('recovery')
309        .onClick(() => {
310          try {
311            // The persistence file is available by default. You can set it as required.
312            let webDownloadItem =
313              webview.WebDownloadItem.deserialize(downloadUtil.strToUint8Array(downloadUtil.readFileSync(filePath, fileName)));
314            webview.WebDownloadManager.resumeDownload(webDownloadItem);
315          } catch (error) {
316            console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);
317          }
318        })
319
320      Web({ src: 'www.example.com', controller: this.controller })
321    }
322  }
323}
324```
325
326Download the task information persistence utility file.
327```ts
328// downloadUtil.ets
329import { util } from '@kit.ArkTS';
330import fileStream from '@ohos.file.fs';
331
332const helper = new util.Base64Helper();
333
334export let filePath : string;
335export const fileName = 'demoFile.txt';
336export namespace  downloadUtil {
337
338  export function init(context: UIContext): void {
339    filePath = context.getHostContext()!.filesDir;
340  }
341
342  export function uint8ArrayToStr(uint8Array: Uint8Array): string {
343    return helper.encodeToStringSync(uint8Array);
344  }
345
346  export function strToUint8Array(str: string): Uint8Array {
347    return helper.decodeSync(str);
348  }
349
350  export function saveDownloadInfo(downloadInfo: string): void {
351    if (!fileExists(filePath)) {
352      mkDirectorySync(filePath);
353    }
354
355    writeToFileSync(filePath, fileName, downloadInfo);
356  }
357
358  export function fileExists(filePath: string): boolean {
359    try {
360      return fileStream.accessSync(filePath);
361    } catch (error) {
362      return false;
363    }
364  }
365
366  export function mkDirectorySync(directoryPath: string, recursion?: boolean): void {
367    try {
368      fileStream.mkdirSync(directoryPath, recursion ?? false);
369    } catch (error) {
370      console.error(`mk dir error. err message: ${error.message}, err code: ${error.code}`);
371    }
372  }
373
374  export function writeToFileSync(dir: string, fileName: string, msg: string): void {
375    let file = fileStream.openSync(dir + '/' + fileName, fileStream.OpenMode.WRITE_ONLY | fileStream.OpenMode.CREATE);
376    fileStream.writeSync(file.fd, msg);
377  }
378
379  export function readFileSync(dir: string, fileName: string): string {
380    return fileStream.readTextSync(dir + '/' + fileName);
381  }
382
383}
384```
385