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