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