Flutter|云點播插件適配鴻蒙(簡單版)

背景

最近游戲社區(qū)在適配鴻蒙系統(tǒng),需要基于鴻蒙版Flutter并適配原生接口,大部分基礎Flutter插件官方已完成鴻蒙化改造,而像視頻上傳使用到的騰訊云點播(VOD)目前尚未支持,為了能正常使用該功能,需要分析源碼、集成鴻蒙版COS SDK自己實現(xiàn)。

代碼實現(xiàn)

oh-package.json5添加COS依賴:

"@tencentcloud/cos":"1.1.4"

新增插件類 FlutterOhosCloudVodUploadSdkPlugin,主要是處理視頻上傳請求:

 onMethodCall(call: MethodCall, result: MethodResult): void {
    let method: string = call.method;
    try {
      switch (method) {
        case VideoUploadConstant.METHOD_UPLOAD_VIDEO:
          const sign: string = call.argument(VideoUploadConstant.PARAM_SIGN);
          const filePath: string = call.argument(VideoUploadConstant.PARAM_SRC_PATH);
          const fileName: string = call.argument(VideoUploadConstant.PARAM_FILE_NAME);
          const coverPath: string = call.argument(VideoUploadConstant.PARAM_COVER);
          const taskId: string = call.argument(VideoUploadConstant.PARAM_TASK_ID);
          this.mgr?.uploadFile(sign, filePath, fileName, coverPath, taskId);
          break;
        default:
          break;
      }
    } catch (err) {
       // 異常處理
    }
  }

實際調用的核心上傳方法:

async uploadFile(sign: string, filePath: string, fileName: string, coverPath: string, taskId: string) {
  .... // 參數校驗等
  let data: ApplyUploadUGCData | null = await this.getApplyUploadUGCData(sign, filePath, fileName, coverPath);
  if (data != null) {
    this.initCosService(this.context, data);
    this.uploadVideo(data, sign, filePath, coverPath, taskId);
  }
}

其中幾個關鍵步驟:

(1)getApplyUploadUGCData:請求上傳票據等數據,請求參數包括簽名、視頻和封面圖的文件名和大小

private async getApplyUploadUGCData(sign: string, filePath: string,
  fileName: string, coverPath: string): Promise<ApplyUploadUGCData | null> {
  let data: ApplyUploadUGCData | null = null;
  try {
    const options: http.HttpRequestOptions = {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'application/json',
      },
      extraData: {
        signature: sign,
        videoName: fileName,
        videoType: fileType,
        videoSize: fileSize,
        coverName: coverName,
        coverType: coverType,
        coverSize: coverSize,
      },
    };
    const url =
      `https://vod2.qcloud.com/v3/index.php?Action=ApplyUploadUGC`;
    const httpRequest = http.createHttp();
    const response = await httpRequest.request(url, options);
    if (response.responseCode === http.ResponseCode.OK) {
      const reply: ApplyUploadUGCReply = JSON.parse(response.result.toString());
      if (reply.code == 0) {
        data = reply.data;
      }
    } 
    httpRequest.destroy();
  } catch (error) {
    // 異常處理
  }
  return data;
}

(2)initCosService:初始化臨時密鑰和VOD COS服務

private initCosService(context: common.Context, data: ApplyUploadUGCData) {
  const credential: QCloudCredential = new QCloudCredential();
  credential.secretID = data.tempCertificate.secretId;
  credential.secretKey = data.tempCertificate.secretKey;
  credential.token = data.tempCertificate.token;
  credential.expirationDate = new Date(data.tempCertificate.expiredTime * 1000);
  this.credential = credential;
  
  const config = new CosXmlServiceConfig(data.storageRegionV5);
  config.retrySleep = 5 * 1000;
  this.service = new CosXmlBaseService(context, config);
}

(3)getMultipartUploadId:獲取視頻分片上傳id

private async getMultipartUploadId(service: CosXmlBaseService, credential: QCloudCredential, bucket: string,
  cosPath: string): Promise<string | null> {
  let uploadId: string | null = null;
  try {
    const putRequest = new InitMultipartUploadRequest(bucket, cosPath);
    putRequest.credential = credential;
    let multipart = await service.initMultipartUpload(putRequest);
    uploadId = multipart.initMultipartUpload?.uploadId ?? null;
  } catch (e) {
     // 異常處理
  }
  return uploadId;
}

(4)uploadVideo:視頻上傳到VOD COS,主要參數有

  • 存儲桶名稱:${data.storageBucket}-${data.storageAppId}
  • 對象在存儲桶中的位置:data.video.storagePath.substring(1)
private async uploadVideo(data: ApplyUploadUGCData, sign: string, filePath: string, coverPath: string,
  taskId: string) {
  .... // 參數校驗等
  const putRequest = new PutObjectRequest(bucket, cosPath, filePath);
  putRequest.credential = this.credential;
  let uploadId = await this.getMultipartUploadId(this.service, this.credential, bucket, cosPath);
  if (uploadId == null) {
    return;
  }
  const task: UploadTask = this.service.upload(putRequest, uploadId, config);
  task.onProgress = (progress: HttpProgress) => {
    this.sendProgressResult(progress.complete, progress.target);
  };
  task.onResult = {
    onSuccess: async (request, result) => {
      if (await VideoUploadUtils.hasCover(coverPath)) {
        this.uploadCover(data, sign, coverPath, taskId);
      } else {
        const uploadData = await this.getCommitUploadUGC(data, sign);
        if (uploadData != null) {
          this.sendSuccessResult(uploadData.video.url, taskId, uploadData.cover.url);
        }
      }
    },
    onFail: (request, error) => {
       // 上傳失敗處理
    }
  }
  task.start();
}

(5)uploadCover:封面圖上傳到VOD COS,比視頻上傳類似且更簡單

private uploadCover(data: ApplyUploadUGCData, sign: string, coverPath: string, taskId: string) {
   .... // 參數校驗等
  const putRequest = new PutObjectRequest(bucket, cosPath, coverPath);
  putRequest.credential = this.credential;
  const task: UploadTask = this.service.upload(putRequest, taskId);
  task.onResult = {
    onSuccess: async (request, result) => {
      const uploadData = await this.getCommitUploadUGC(data, sign);
      if (uploadData != null) {
        this.sendSuccessResult(uploadData.video.url, taskId, uploadData.cover.url);
      } 
    },
    onFail: (request, error) => {
       // 上傳失敗處理
    }
  }
  task.start();
}

(6)getCommitUploadUGC:請求視頻和封面圖鏈接,請求參數包括簽名、視頻id

private async getCommitUploadUGC(data: ApplyUploadUGCData, sign: string): Promise<CommitUploadUGCCData | null> {
  let uploadData: CommitUploadUGCCData | null = null;
  try {
    const options: http.HttpRequestOptions = {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'application/json',
      },
      extraData: {
        signature: sign,
        vodSessionKey: data.vodSessionKey,
      },
    };
    const url =
      `https://vod2.qcloud.com/v3/index.php?Action=CommitUploadUGC`;
    const httpRequest = http.createHttp();
    const response = await httpRequest.request(url, options);
    if (response.responseCode === http.ResponseCode.OK) {
      const reply: CommitUploadUGCReply = JSON.parse(response.result.toString());
      if (reply.code == 0) {
        uploadData = reply.data;
      }
    }
    httpRequest.destroy();
  } catch (error) {
      // 異常處理
  }
  return uploadData;
}

最后在Flutter 插件pubspec.yaml聲明鴻蒙接口:

plugin:
  platforms:
    android:
      package: com.sirius.cloud_vod_upload_sdk
      pluginClass: FlutterCloudVodUploadSdkPlugin
    ios:
      pluginClass: TencentFlutterCloudVodUploadSdkPlugin
    ohos:
      pluginClass: FlutterOhosCloudVodUploadSdkPlugin

代碼地址:https://github.com/minmin1123/flutter_cloud_vod_upload_sdk

使用說明

參考官方Flutter 上傳 SDK先接入到Flutter項目中:

(1)將前面的源碼復制到項目中,并在pubspec.yaml中引入,比如:

flutter_cloud_vod_upload_sdk:
  path: ../flutter_cloud_vod_upload_sdk

(2)申請上傳簽名:參考官方指引

(3)創(chuàng)建任務UploadTask并上傳,任務參數:

  • taskId:任務唯一id
  • signature:上傳簽名
  • fileName:視頻文件名
  • filePath:視頻本地路徑
  • coverPath:封面本地路徑
static Future<UploadTask> uploadVideo(
  UploadTaskController controller,
  String taskId,
  String signature,
  String fileName,
  String filePath,
  String coverPath, {
  ValueChanged<String>? onStart,
  UploadProgressCallBack? onProgress,
  ValueChanged<UploadTaskCompleteInfo>? onSuccess,
  ValueChanged<UploadTaskCompleteInfo>? onFail,
}) async {
  var task = UploadTask(
    taskId: taskId,
    signature: signature,
    fileName: fileName,
    filePath: filePath,
    coverPath: coverPath,
    onStart: onStart,
    onProgress: onProgress,
    onFail: onFail,
    onSuccess: onSuccess,
  );
  controller.addTask(task);
  return task;
}

(4)上傳結果回調在UploadTaskCompleteInfo,包括:

  • videoId:視頻文件id
  • videoURL:視頻存儲地址
  • coverURL:封面存儲地址
  • retCode:錯誤碼
  • descMsg:錯誤描述信息

總結

如此實現(xiàn)了一個簡單鴻蒙版VOD Flutter插件,但時間原因并沒有把源碼中所有功能都還原,后續(xù)有時間會持續(xù)優(yōu)化:

  • 預上傳
  • 大文件分塊上傳
  • 斷點續(xù)傳
  • QUIC
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容