最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

uniapp實(shí)現(xiàn)圖片壓縮并上傳的實(shí)戰(zhàn)步驟

 更新時(shí)間:2026年02月27日 09:19:20   作者:JunjunZ  
本文介紹了如何在uniapp中封裝一個(gè)既能支持H5和小程序雙平臺(tái),又能實(shí)現(xiàn)圖片自動(dòng)壓縮并處理接口響應(yīng)異常的自定義上傳方法useUploadMethod,文章詳細(xì)分析了痛點(diǎn),提出了整體架構(gòu)設(shè)計(jì),需要的朋友可以參考下

最近在使用uniapp開發(fā)時(shí),有個(gè)功能既要支持H5和小程序雙平臺(tái),又要實(shí)現(xiàn)圖片自動(dòng)壓縮,還要處理好接口響應(yīng)的各種異常情況。最終封裝了這個(gè) useUploadMethod 自定義上傳方法,今天分享給大家。

痛點(diǎn)分析

先看看我們平時(shí)會(huì)遇到哪些問題:

// 痛點(diǎn)1:圖片太大,上傳慢
uni.uploadFile({
  filePath: 'big-image.jpg'  // 5MB的圖片直接上傳
  // 用戶等得花兒都謝了
})

// 痛點(diǎn)2:登錄態(tài)過期
uni.uploadFile({
  success: (res) => {
    // {"code":405,"msg":"未登錄"}
    // 啥也沒發(fā)生,用戶繼續(xù)操作,然后報(bào)錯(cuò)
  }
})

// 痛點(diǎn)3:H5和小程序API不統(tǒng)一
// H5用 File/Blob
// 小程序用 tempFilePath
// 代碼里到處都是 #ifdef

技術(shù)方案

1. 整體架構(gòu)

整個(gè)上傳方法分為三個(gè)核心層:

  • 預(yù)處理層:圖片壓縮、參數(shù)組裝
  • 上傳層:跨平臺(tái)上傳、進(jìn)度監(jiān)聽
  • 響應(yīng)層:狀態(tài)碼處理、登錄態(tài)管理

2. 圖片壓縮模塊

跨平臺(tái)壓縮策略

async function compressImage(file: UploadFileItem, options: any): Promise<File | string> {
  // 未啟用壓縮,直接返回
  if (!options?.enabled) return file.url

  // H5平臺(tái):使用 compressorjs
  // #ifdef H5
  return compressImageH5(file, options)
  // #endif

  // 小程序平臺(tái):使用 uni.compressImage
  // #ifndef H5
  return new Promise((resolve) => {
    uni.compressImage({
      src: file.url,
      quality: options.quality || 80,
      width: options.maxWidth,
      height: options.maxHeight,
      success: (res) => resolve(res.tempFilePath),
      fail: () => resolve(file.url) // 壓縮失敗回退原圖
    })
  })
  // #endif
}

設(shè)計(jì)亮點(diǎn)

  • 條件編譯處理平臺(tái)差異
  • 壓縮失敗自動(dòng)降級(jí)使用原圖
  • 統(tǒng)一返回類型,上層無感知

H5平臺(tái)深度優(yōu)化(compressorjs)

async function compressImageH5(file: UploadFileItem, options?: CompressOptions): Promise<File | string> {
  let { name: fileName, url: filePath } = file
  
  return new Promise((resolve) => {
    // 從blob URL獲取文件
    fetch(filePath)
      .then(res => res.blob())
      .then((blob) => {
        // compressorjs壓縮配置
        new Compressor(blob, {
          quality: (options?.quality || 80) / 100, // 轉(zhuǎn)換為0-1范圍
          maxWidth: options?.maxWidth,
          maxHeight: options?.maxHeight,
          mimeType: blob.type,
          success: (compressedBlob) => {
            // 生成標(biāo)準(zhǔn)File對(duì)象
            const fileName = `file-${Date.now()}.${blob.type.split('/')[1]}`
            const file = new File([compressedBlob], fileName, { type: blob.type })
            resolve(file)
          },
          error: () => resolve(filePath) // 壓縮失敗回退
        })
      })
      .catch(() => resolve(filePath))
  })
}

關(guān)鍵點(diǎn)

  • fetch + blob() 獲取原始文件數(shù)據(jù)
  • compressorjs 提供高質(zhì)量的圖片壓縮
  • 返回 File 對(duì)象,H5上傳更標(biāo)準(zhǔn)

3. 核心上傳方法

export function useUploadMethod(httpOptions: HttpOptions) {
  const { url, name, formData: data, header, timeout, onStart, onFinish, onSuccess, compress } = httpOptions

  const uploadMethod: UploadMethod = async (file, formData, options) => {
    // 1. 上傳開始鉤子
    onStart?.()

    // 2. 圖片壓縮(如果啟用)
    let filePath = file.url
    try {
      filePath = await compressImage(file, compress)
    } catch {
      filePath = file.url // 異常降級(jí)
    }

    // 3. 創(chuàng)建上傳任務(wù)
    const uploadTask = uni.uploadFile({
      url: options.action || url,
      header: { ...header, ...options.header },
      name: options.name || name,
      formData: { ...data, ...formData },
      timeout: timeout || 60000,
      
      // 4. 跨平臺(tái)文件參數(shù)處理
      ...(typeof File !== 'undefined' && filePath instanceof File 
          ? { file: filePath }   // H5: File對(duì)象
          : { filePath }),       // 小程序: 路徑字符串

      // 5. 響應(yīng)處理
      success: (res) => handleSuccess(res, file, options),
      fail: (err) => handleError(err, file, options)
    })

    // 6. 進(jìn)度監(jiān)聽
    uploadTask.onProgressUpdate((res) => {
      options.onProgress(res, file)
    })
  }

  return { uploadMethod }
}

4. 智能響應(yīng)處理器

// 上傳成功處理
function handleSuccess(res: any, file: UploadFileItem, options: any) {
  try {
    // 解析響應(yīng)數(shù)據(jù)
    const resData = JSON.parse(res.data) as ResData<any>
    
    // 狀態(tài)碼檢查
    if (res.statusCode >= 200 && res.statusCode < 300) {
      const { code, msg: errMsg = '上傳失敗' } = resData
      
      if (+code === 200) {
        // 上傳成功
        options.onSuccess(res, file, resData)
        onSuccess?.(res, file, resData)
        return
      }
      
      // 登錄態(tài)過期處理
      if (+code === 405 || errMsg.includes('未登錄')) {
        toast.show(errMsg || '登錄態(tài)失效')
        logout()
        login() // 自動(dòng)跳轉(zhuǎn)登錄頁(yè)
        return
      }
      
      // 其他業(yè)務(wù)錯(cuò)誤
      toast.show(errMsg)
      options.onError({ ...res, errMsg }, file, resData)
      return
    }
    
    // HTTP 401處理
    if (res.statusCode === 401) {
      toast.show('登錄態(tài)失效')
      logout()
      login()
      return
    }
    
    // 其他HTTP錯(cuò)誤
    toast.show(resData.msg || `服務(wù)出錯(cuò):${res.statusCode}`)
    options.onError({ ...res, errMsg: '服務(wù)開小差了' }, file)
    
  } finally {
    onFinish?.() // 無論成功失敗都調(diào)用
  }
}

// 上傳失敗處理
function handleError(err: any, file: UploadFileItem, options: any) {
  try {
    toast.show('網(wǎng)絡(luò)錯(cuò)誤,請(qǐng)稍后再試')
    // 設(shè)置上傳失敗
    options.onError(err, file, formData)
  } finally {
    // 文件上傳完成時(shí)調(diào)用
    onFinish?.()
  }
} as any)

基礎(chǔ)用法

<template>
  <wd-upload
    :upload-method="uploadMethod"
    v-model:file-list="fileList"
    @change="handleChange"
  />
</template>

<script setup>
import { useUploadMethod } from './upload-method'

// 配置上傳方法
const { uploadMethod } = useUploadMethod({
  url: '/api/upload',
  name: 'file',
  header: {
    'Authorization': 'Bearer ' + getToken()
  },
  // 圖片壓縮配置
  compress: {
    enabled: true,
    quality: 80,
    maxWidth: 1920,
    maxHeight: 1080
  },
  // 鉤子函數(shù)
  onStart: () => console.log('開始上傳'),
  onSuccess: (res, file) => console.log('上傳成功', file),
  onFinish: () => console.log('上傳完成')
})
</script>

以上就是uniapp實(shí)現(xiàn)圖片壓縮并上傳的實(shí)戰(zhàn)步驟的詳細(xì)內(nèi)容,更多關(guān)于uniapp圖片壓縮并上傳的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

颍上县| 东乌珠穆沁旗| 石林| 南康市| 泗洪县| 武川县| 武川县| 方城县| 景宁| 临城县| 宜州市| 揭东县| 水富县| 青河县| 灵石县| 新巴尔虎左旗| 清涧县| 苏尼特左旗| 温泉县| 洪泽县| 石城县| 闵行区| 双牌县| 宝应县| 大名县| 永登县| 东兰县| 德安县| 延长县| 鄢陵县| 务川| 夏邑县| 黔南| 阜阳市| 雅安市| 逊克县| 龙山县| 宜川县| 宝坻区| 龙川县| 盐津县|