鸿蒙下载文件(支持断点续传)

143 阅读1分钟
  • sdk 11 以上版本

import { rcp } from '@kit.RemoteCommunicationKit';  
  
import { OtaInfoResponse, ResponseCode } from './Utils';  
import fs from '@ohos.file.fs';  
import { BusinessError } from '@ohos.base';  
  
/**  
 * sdk 11 以上才能用
 */
 
 export class DownloadUtils {  
  private session: rcp.Session | null = null  
  private config: rcp.Configuration = {  
    transfer: {  
      timeout: {  
        connectMs: 1000 * 60 * 20,  
        transferMs: 1000 * 60 * 20  
      }  
    }  };  
  
  /**  
   * 获取下载文件大小  
   * @param url  固件下载地址  
   * @returns 文件大小  
   */  
   getFileSize(url: string): Promise<number> {  
    const session = rcp.createSession({  
      requestConfiguration: this.config  
    });  
    return session.head(url)  
      .then(res => {  
        const contentLength = res.headers['content-length'];  
        return contentLength ? Number(contentLength) : 0;  
      })  
      .finally(() => {  
        session.close();  
      });  
  }  
  
  
  /**  
   * 开始下载文件  
   * @param url 下载地址  
   * @param onProgress 更新进度  
   * @param onDownloadSuccess 下载完成  
   * @param onDownloadFail 下载失败  
   * example url: https://cdn-hybrid-prod.hunyuan.tencent.com/Desktop/business/channels/yuanbao_9019_x64.exe   */  
async startDownLoad(  
    url: string,  
    onProgress?: (downloaded: number, total: number) => void,  
    onDownloadSuccess?: () => void,  
    onDownloadFail?: (e: Error) => void  
  ) {  
    let downloadedSize = 0  
    let totalSize = await this.getFileSize(url)  
    try {  
      let file = fs.openSync(getContext().filesDir + '/test.exe', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);  
      if (fs.accessSync(file.path)) { //是否存在,读取已经下载字节  
        downloadedSize = fs.statSync(file.fd).size  
        console.info("File Size==" + downloadedSize)  
      }  
      const sessionConfig: rcp.SessionConfiguration = {  
        //断点续传  
        requestConfiguration: this.config,  
        headers: {  
          // 关键:添加Range头  
          'Range': `bytes=${downloadedSize}-`  
        }  
      }  
      const writeSync: (buffer: ArrayBuffer) => void = buffer => {  
        fs.writeSync(file.fd, buffer, { offset: downloadedSize })  
        downloadedSize += buffer.byteLength  
        if (onProgress) {  
          onProgress(downloadedSize, totalSize)  
        }  
      }      
      let downloadToStream: rcp.DownloadToStream = {  
        kind: 'stream',  
        stream: { writeSync }  
      }      
      this.session = rcp.createSession(sessionConfig);  
      this.session?.downloadToStream(url, downloadToStream)  
        .then(() => {  
          console.info(`Succeeded in getting the response`);  
          onDownloadSuccess?.()  
        }).catch((err: BusinessError) => {  
        console.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);  
        this.session?.close()  
        onDownloadFail?.(err)  
      });  
    } catch (e) {  
      console.error('error openSync' + JSON.stringify(e))  
    }  
  }  
  /**  
   * 停止下载文件  
   */  
   stopDownLoad() {  
    this.session?.cancel()  
  }  
}  
let otaUtil = new DownloadUtils();  
export default otaUtil as DownloadUtils


  • sdk 10 版本

export class DownloadUtils {  
  private httpRequest: http.HttpRequest | null = null;  
  private totalSize: number = 0;  
  private downloadedSize: number = 0;  
  private isDownloading: boolean = false  
  
  constructor() {  
  }  
  
  // 获取文件总大小  
  private async getFileSize(url: string): Promise<void> {
    const session = http.createHttp()  
    const headResponse = await session.request(url, {  
      method: http.RequestMethod.HEAD  
    });  
    this.totalSize = parseInt(headResponse?.header['content-length']) || 0;  
    console.log(`文件总大小: ${this.totalSize} 字节`);  
    session.destroy()  
  }  
  
  // 开始/恢复下载  
  async startDownLoad(  
    url: string,  
    path: string,  
    onProgress?: (progress: number) => void,  
    onDownloadSuccess?: () => void,  
    onDownloadFail?: (e: Error) => void  
  ) {  
    if (this.isDownloading) {  
      return  
    }  
    this.httpRequest?.destroy()  
    this.httpRequest = http.createHttp()  
    await this.getFileSize(url);  
    // 如果已下载完成  
    if (this.downloadedSize >= this.totalSize) {  
      console.log('文件已下载完成');  
      return;  
    }  
    const file = fs.openSync(path, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);  
    if (fs.accessSync(file.path)) {  
      this.downloadedSize = fs.statSync(file.fd).size  
      console.warn("downloadedSize==" + this.downloadedSize)  
    }  
    // 注册数据接收监听  
    this.httpRequest?.on('dataReceive', (data: ArrayBuffer) => {  
      // 将数据写入指定位置  
      fs.writeSync(file.fd, data, {  
        offset: this.downloadedSize  
      });  
      this.downloadedSize += data.byteLength;  
  
      const progress = Math.round((this.downloadedSize / this.totalSize) * 100)  
      console.log(`进度中: ${progress}%`);  
      onProgress?.(progress)  
      if (this.downloadedSize == this.totalSize && this.downloadedSize != 0) {  
        onDownloadSuccess?.()  
      }  
    });  
    try {  
      this.isDownloading = true  
      console.warn(`Range 请求头: bytes=${this.downloadedSize}-`);  
      await this.httpRequest?.requestInStream(url, {  
        method: http.RequestMethod.GET,  
        expectDataType: http.HttpDataType.ARRAY_BUFFER,  
        header: {  
          'Range': `bytes=${this.downloadedSize}-`  
        }  
      });  
    } catch (err) {  
      this.isDownloading = false  
      console.error(`下载失败: ${(err as BusinessError).message}`);  
      onDownloadFail?.(err)  
    } finally {  
      fs.closeSync(file);  
      this.httpRequest?.off('dataReceive'); // 取消监听  
      this.httpRequest?.destroy();  
      if (this.downloadedSize >= this.totalSize) {  
        console.warn('下载完成')  
      }  
      this.isDownloading = false  
    }  
  }

	
  stopDownLoad() {  
    this.httpRequest?.destroy()
    this.isDownloading = false 
  }  
}