Просмотр исходного кода

fix(mp): 上传前压缩大图并延长上传超时,修复大图上传中断失败

Xiaogang Liao 4 дней назад
Родитель
Сommit
f74f0453a2

+ 46 - 0
cfc-frontend/__tests__/utils/image-picker.test.js

@@ -0,0 +1,46 @@
+import { compressPickedImages } from '../../utils/image-picker'
+
+function run(paths, files) {
+  return new Promise(resolve => {
+    compressPickedImages(paths, files, resolve)
+  })
+}
+
+describe('image-picker 上传前压缩', () => {
+  afterEach(() => {
+    delete global.uni
+  })
+
+  it('小于 800KB 的图片不压缩', async () => {
+    global.uni = { compressImage: jest.fn() }
+    const result = await run(['a.jpg'], [{ size: 100 * 1024 }])
+    expect(result).toEqual(['a.jpg'])
+    expect(global.uni.compressImage).not.toHaveBeenCalled()
+  })
+
+  it('大图压缩成功时返回压缩后的临时路径', async () => {
+    global.uni = {
+      compressImage: jest.fn(opts => opts.success({ tempFilePath: 'a-compressed.jpg' }))
+    }
+    const result = await run(['a.jpg'], [{ size: 6 * 1024 * 1024 }])
+    expect(result).toEqual(['a-compressed.jpg'])
+    expect(global.uni.compressImage).toHaveBeenCalledWith(expect.objectContaining({
+      src: 'a.jpg',
+      quality: 75
+    }))
+  })
+
+  it('压缩失败时回退原图', async () => {
+    global.uni = {
+      compressImage: jest.fn(opts => opts.fail(new Error('compress failed')))
+    }
+    const result = await run(['a.jpg'], [{ size: 6 * 1024 * 1024 }])
+    expect(result).toEqual(['a.jpg'])
+  })
+
+  it('未知大小或缺少压缩 API 时不影响选择结果', async () => {
+    global.uni = {}
+    const result = await run(['a.jpg', 'b.png'], [{}, {}])
+    expect(result).toEqual(['a.jpg', 'b.png'])
+  })
+})

+ 3 - 0
cfc-frontend/manifest.json

@@ -5,6 +5,9 @@
   "versionName": "1.0.0",
   "versionCode": "100",
   "transformPx": false,
+  "networkTimeout": {
+    "uploadFile": 180000
+  },
   "app-plus": {
     "nvueStyleCompiler": "uni-app",
     "compilerVersion": 3,

+ 80 - 1
cfc-frontend/utils/image-picker.js

@@ -1,3 +1,53 @@
+// 上传前压缩:小程序默认上传超时 60 秒,手机原图/聊天记录图片较大时容易超时中断。
+// 超过 800KB 的图片先用 compressImage 压到约 75% 质量,失败则回退原图。
+var COMPRESS_THRESHOLD = 800 * 1024
+
+export function compressPickedImages(paths, files, callback) {
+  var result = []
+  var index = 0
+
+  function finish() {
+    callback(result)
+  }
+
+  function next() {
+    if (index >= paths.length) {
+      finish()
+      return
+    }
+    var i = index++
+    var path = paths[i]
+    var file = files && files[i]
+    var size = file && typeof file.size === 'number' ? file.size : 0
+
+    if (!path || (size > 0 && size <= COMPRESS_THRESHOLD)
+        || typeof uni === 'undefined' || typeof uni.compressImage !== 'function') {
+      result[i] = path
+      next()
+      return
+    }
+
+    uni.compressImage({
+      src: path,
+      quality: 75,
+      success: function (res) {
+        result[i] = (res && res.tempFilePath) || path
+        next()
+      },
+      fail: function () {
+        result[i] = path
+        next()
+      }
+    })
+  }
+
+  if (!paths || !paths.length) {
+    callback([])
+    return
+  }
+  next()
+}
+
 /**
  * 统一图片选择器(微信小程序支持「从聊天记录选择图片」)
  *
@@ -33,11 +83,40 @@ export function pickImage(options) {
   var fail = opts.fail
   var complete = opts.complete
 
-  function onSuccess(res) {
+  function finish(res) {
     if (success) success(res)
     if (complete) complete(res)
   }
 
+  function onSuccess(res) {
+    var paths = (res && res.tempFilePaths) || []
+    if (!paths.length) {
+      finish(res)
+      return
+    }
+    compressPickedImages(paths, (res && res.tempFiles) || [], function (compressedPaths) {
+      var out = {}
+      for (var key in res) {
+        if (Object.prototype.hasOwnProperty.call(res, key)) out[key] = res[key]
+      }
+      out.tempFilePaths = compressedPaths
+      if (res && res.tempFiles) {
+        var files = []
+        for (var i = 0; i < res.tempFiles.length; i++) {
+          var file = res.tempFiles[i] || {}
+          var copy = {}
+          for (var k in file) {
+            if (Object.prototype.hasOwnProperty.call(file, k)) copy[k] = file[k]
+          }
+          copy.path = compressedPaths[i] || copy.path
+          files.push(copy)
+        }
+        out.tempFiles = files
+      }
+      finish(out)
+    })
+  }
+
   function onFail(err) {
     if (fail) fail(err)
     if (complete) complete(err)