?一、鴻蒙實現處理-壓縮上傳整體代碼處理邏輯
- 轉沙箱
- 壓縮
- 獲取憑證并上傳文件
- 文件準備(拿到文件流)
- 獲取上傳憑證(調接口1拿到file_name和upload_url)
- 執行文件上傳(向階段2拿到的upload_url上傳文件)
- 更新列表數據 (將階段2獲取到的file_name更新進去,最后提交表單時傳參)
- 提交表單
// 處理視頻/圖片(轉沙箱、壓縮、調接口獲取url、上傳到OSS)// index不是undefined時:修改async handleFile(messageInfo: MessageInfo, index?: number) {loadingInstance.show()if (this.videoFlag) {// 1視頻處理// 1)轉沙箱const file = uriToSandBox(messageInfo.sourceFilePath);// 2)壓縮const fileUri = await this.videoToCompress(file.fileUri).catch(() => {loadingInstance.hide()showToast('視頻壓縮失敗');});let stat = fs.statSync(fileUri as string);if (stat.size / 1024 / 1024 > 50) {loadingInstance.hide()showToast('請拍攝50M以內的視頻');return;}// 3)獲取上傳憑證并上傳fileUri && this.fileUpload(fileUri, messageInfo, index)} else {// 2圖片處理const file = uriToSandBox(messageInfo.sourceFilePath);// 1)轉沙箱并壓縮const fileUri = await this.transferFile(messageInfo.sourceFilePath).catch(() => {loadingInstance.hide()});// 2)獲取上傳憑證并上傳fileUri && this.fileUpload(fileUri, messageInfo, index)}}
二、壓縮部分
實現思路:
圖片壓縮
packing二分方式循環壓縮
視頻壓縮
三種方案
代碼:
// 優先壓縮圖片質量
async qualityPriorityCompress(sourcePixelMap: image.PixelMap, maxCompressedImageSize: number) {let compressedImageData: ArrayBuffer =await this.packing(sourcePixelMap, IMAGE_QUALITY_ZERO, this.afterCompressFmt);// 先判斷圖片質量參數設置最低0能否滿足目標大小。如果能滿足目標大小,則直接使用packing二分圖片質量。如果不滿足,則質量參數固定為0,進行scale尺寸壓縮if (compressedImageData.byteLength <= maxCompressedImageSize * BYTE_CONVERSION) {// 滿足目標大小,直接使用packing二分await this.packingImage(sourcePixelMap, compressedImageData, maxCompressedImageSize * BYTE_CONVERSION);} else {// 不滿足目標大小,質量參數設置為0,再進行scale尺寸壓縮await this.scalePriorityCompress(sourcePixelMap, maxCompressedImageSize, IMAGE_QUALITY_ZERO);}// 更新顯示壓縮后的圖片格式this.showCompressFormat = this.afterCompressFmt;
}// packing二分方式循環壓縮
async packingImage(sourcePixelMap: image.PixelMap, compressedImageData: ArrayBuffer, maxCompressedImageByte: number) {let imageQuality: number = 0;const DICHOTOMY_ACCURACY = this.minBisectUnit;// 圖片質量參數范圍為0-100,這里以minBisectUnit為最小二分單位創建用于packing二分圖片質量參數的數組。const packingArray: number[] = [];// 性能知識點: 如果對圖片壓縮質量要求不高,建議調高minBisectUnit(對應‘packing最小二分單位’),減少循環,提升packing壓縮性能。for (let i = 0; i <= 100; i += DICHOTOMY_ACCURACY) {packingArray.push(i);}let left = 0; // 定義二分搜索范圍的左邊界let right = packingArray.length - 1; // 定義二分搜索范圍的右邊界const AFTER_COMPRESS_FMT = this.afterCompressFmt;// 二分壓縮圖片while (left <= right) {const mid = Math.floor((left + right) / 2); // 定義二分搜索范圍的中間位置imageQuality = packingArray[mid]; // 獲取二分中間位置的圖片質量值// 根據傳入的圖片質量參數進行packing壓縮,返回壓縮后的圖片文件流數據。compressedImageData = await this.packing(sourcePixelMap, imageQuality, AFTER_COMPRESS_FMT);// 判斷查找一個盡可能接近但不超過壓縮目標的壓縮大小if (compressedImageData.byteLength <= maxCompressedImageByte) {// 二分目標值在右半邊,繼續在更高的圖片質量參數(即mid + 1)中搜索left = mid + 1;// 判斷mid是否已經二分到最后,如果二分完了,退出if (mid === packingArray.length - 1) {break;}// 獲取下一次二分的圖片質量參數(mid+1)壓縮的圖片文件流數據compressedImageData = await this.packing(sourcePixelMap, packingArray[mid + 1], AFTER_COMPRESS_FMT);// 判斷用下一次圖片質量參數(mid+1)壓縮的圖片大小是否大于指定圖片的壓縮目標大小。如果大于,說明當前圖片質量參數(mid)壓縮出來的// 圖片大小最接近指定圖片的壓縮目標大小。傳入當前圖片質量參數mid,得到最終目標圖片壓縮數據。if (compressedImageData.byteLength > maxCompressedImageByte) {compressedImageData = await this.packing(sourcePixelMap, packingArray[mid], AFTER_COMPRESS_FMT);break;}} else {// 目標值不在當前范圍的右半部分,將搜索范圍的右邊界向左移動,以縮小搜索范圍并繼續在下一次迭代中查找左半部分。right = mid - 1;}}// ...
}
三、OSS上傳文件部分
方案圖:
?
?代碼:
鴻蒙環境服務端簽名直傳_對象存儲(OSS)-阿里云幫助中心
// 獲取上傳憑證并上傳// index不是undefined時:修改private fileUpload = async (fileUrl: string, messageInfo: MessageInfo, index?: number) => {let fileInfo: fs.File | null = null;try {// 階段1:文件準備fileInfo = await fs.open(fileUrl, fs.OpenMode.READ_ONLY);const fileStat = await fs.stat(fileInfo.fd);const fileData = new ArrayBuffer(fileStat.size);await fs.read(fileInfo.fd, fileData);// 階段2:獲取上傳憑證(調接口1拿到file_name和upload_url)const fileType = this.videoFlag ? 'mp4' : 'jpg';const uploadInfo = await this.getUploadCredentials(fileType);// 階段3:執行文件上傳(向階段2拿到的upload_url上傳文件)await this.uploadFileToOSS(uploadInfo.upload_url, fileData, fileStat.size)// 階段4:更新列表數據 (將階段2獲取到的file_name更新進去,最后提交表單時傳參)this.updateMediaLists(uploadInfo.file_name, messageInfo, index);return true;} catch (error) {showToast('文件上傳失敗,請重新上傳')return false;} finally {// 確保資源釋放if (fileInfo?.fd) {await fs.close(fileInfo.fd);}loadingInstance.hide();}};