使用WebUploader解決安卓微信瀏覽器上傳圖片中遇到的bug

摘自我的博客:走啊走的記錄,歡迎點擊查看,效果更佳!html

微信瀏覽器上傳圖片bug的緣由

微信在新版本中採用的是本身的X5內核瀏覽器,而在較老的版本中還有多是安卓的原生瀏覽器。具體的環境我也不太瞭解,可是通過實際多臺安卓機型的測試,我採起的方案能夠基本確保在安卓機中微信瀏覽器的成功上傳。蘋果機型沒問題,由於微信的ios客戶端使用的是Safari的內核,沒有各類坑,且效果最好。前端

這裏給出一個 WebUploader 官方關於移動端適配的 issues 連接。裏面提供的方法確實有效,但就是解決的方案並無很清楚的展現出來,從該issues中有好幾我的用戶提出如何修改就能知道了。html5

開始時遇到的問題

環境

後臺使用 Spring MVC [V 4.08],前端使用一個開源的 HTML5 框架java

問題

ios可完美上傳,安卓手機一半以上不太支持,出現進度條卡死,圖片沒法上傳成功並且只能上傳png格式圖片的問題(後來證實是因爲壓縮失敗引發的,在解決中詳細指出)。發佈到服務器上正式運營之後,發現部分用戶只填寫了文字信息,沒法上傳圖片,很差統計數據,可是這樣的 BUG 率顯然是不行的,接下來就給出個人解決方案吧,通過實際測試應該是沒問題的,不保證徹底有效,由於原理不是太清楚,僅供參考。android

後來的解決方案

第一步,sendAsBinary: true

我先是按照 issues 中給出的第一個解決方法,設置 sendAsBinary: true,後臺不作任何修改的狀況下會產生 500 的錯誤,可是此時解決了進度條卡死的問題(固然啊!圖片直接就上傳失敗了!)……根據issues中 2betop 的回答,此時獲取文件應該是直接獲取文件的二進制流。ios

以前獲取圖片的方式是使用 Spring 自帶的 MultipartHttpServletRequestHttpServletRequest 的實例 request 轉換,而後獲取多個文件的信息。下方代碼根據實際代碼刪減沒必要要的細枝末節得出。git

MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;

        // 獲取上傳文件的列表
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();

        // 圖片上傳前的原始名
        String originalName;

        // 循環獲取多文件上傳時文件列表中的每一個文件對象
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {

            // 上傳文件
            MultipartFile mf = entity.getValue();

            // 文件上傳前的原始名
            originalName = mf.getOriginalFilename();

            // 文件擴展名
            String fileExt = originalName.substring(originalName.lastIndexOf(".") + 1).toLowerCase();

            // 文件的絕對路徑File
            File uploadFile = new File(uploadAbsolutePath + "/" + newName);

            try {
                FileCopyUtils.copy(mf.getBytes(), uploadFile);
            } catch (IOException ioException) {
                logger.error("圖片保存到文件夾中出錯!", ioException);
            } catch (Exception e) {
                logger.error("文件沒有複製到指定的目錄下", e);
            }

        }

這是本來的獲取方式,500 報錯時指示是第一行代碼出錯,沒法轉換,由於此時 WebUploader 在設置了 sendAsBinary: true後 並無使用 content-type: multipart/form-data 上傳文件,而是 content-type: application/ocet-stream,源碼中也是這麼寫的,可是實際獲取的請求頭中並無看到這個字段,而只是圖片的類型.下列給出我使用 Chrome 的 devTools 保存下來的請求信息,只貼出 headers 中的字段值(針對同一個上傳 API 提出請求):github

500 錯誤時的請求頭web

"url": "http://localhost:8787/lostFound/front/release/upload?releaseType=0&orderId=330&id=WU_FILE_1&name=20140120_035024000_iOS.jpg&type=image%2Fjpeg&lastModifiedDate=Sat+Jan+31+2015+01%3A32%3A34+GMT%2B0800+(%C3%A4%C2%B8%C2%AD%C3%A5%C2%9B%C2%BD%C3%A6%C2%A0%C2%87%C3%A5%C2%87%C2%86%C3%A6%C2%97%C2%B6%C3%A9%C2%97%C2%B4)&size=81666",
          "httpVersion": "HTTP/1.1",
          "headers": [
            {
              "name": "Origin",
              "value": "http://localhost:8787"
            },
            {
              "name": "Accept-Encoding",
              "value": "gzip, deflate"
            },
            {
              "name": "Host",
              "value": "localhost:8787"
            },
            {
              "name": "Accept-Language",
              "value": "zh-CN,zh;q=0.8"
            },
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
            },
            {
              "name": "Content-Type",
              "value": "image/jpeg"
            },
            {
              "name": "Accept",
              "value": "*/*"
            },
            {
              "name": "Referer",
              "value": "http://localhost:8787/lostFound/"
            },
            {
              "name": "Cookie",
              "value": "JSESSIONID=2839511C91E9ECE62D155C6EE18F3259; JSESSIONID=64B53863E1C7D82B96927F298A864E18"
            },
            {
              "name": "Connection",
              "value": "keep-alive"
            },
            {
              "name": "Content-Length",
              "value": "81666"
            }
          ],
          "queryString": [
            {
              "name": "releaseType",
              "value": "0"
            },
            {
              "name": "orderId",
              "value": "330"
            },
            {
              "name": "id",
              "value": "WU_FILE_1"
            },
            {
              "name": "name",
              "value": "20140120_035024000_iOS.jpg"
            },
            {
              "name": "type",
              "value": "image%2Fjpeg"
            },
            {
              "name": "lastModifiedDate",
              "value": "Sat+Jan+31+2015+01%3A32%3A34+GMT%2B0800+(%C3%A4%C2%B8%C2%AD%C3%A5%C2%9B%C2%BD%C3%A6%C2%A0%C2%87%C3%A5%C2%87%C2%86%C3%A6%C2%97%C2%B6%C3%A9%C2%97%C2%B4)"
            },
            {
              "name": "size",
              "value": "81666"
            }
          ],
          "bodySize": 0

在不修改 sendAsBinary: true 以前成功上傳的請求頭json

"url": "http://localhost:8787/lostFound/front/release/upload",
          "httpVersion": "HTTP/1.1",
          "headers": [
            {
              "name": "Origin",
              "value": "http://localhost:8787"
            },
            {
              "name": "Accept-Encoding",
              "value": "gzip, deflate"
            },
            {
              "name": "Host",
              "value": "localhost:8787"
            },
            {
              "name": "Accept-Language",
              "value": "zh-CN,zh;q=0.8"
            },
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
            },
            {
              "name": "Content-Type",
              "value": "multipart/form-data; boundary=----WebKitFormBoundaryLeVpfViKLf1xLdIr"
            },
            {
              "name": "Accept",
              "value": "*/*"
            },
            {
              "name": "Referer",
              "value": "http://localhost:8787/lostFound/"
            },
            {
              "name": "Cookie",
              "value": "JSESSIONID=A76C40D04276501F54675AA02AE61467; JSESSIONID=64B53863E1C7D82B96927F298A864E18"
            },
            {
              "name": "Connection",
              "value": "keep-alive"
            },
            {
              "name": "Content-Length",
              "value": "44210"
            }
          ],
          "queryString": [],
          "bodySize": 929,

比較二者的區別,發現區別:

  • content-type: 修改成 sendAsBinary: true 之後,這個值變爲 image/jpeg, 而以前是 multipart/form-data,因此不能再使用 MultipartHttpServletRequest,後端獲取改成獲取文件流。

  • queryString:啓用二進制上傳之後,參數直接添加到 Url 中。

  • bodySize:啓用以後變爲 0,啓用以前不爲 0

先修改後端獲取方式,代碼更改以下:

File file = new File(uploadAbsolutePath);

        if (!file.exists() && !file.mkdirs()) {
            // 若是file對象不存在,那麼就將該對象的路徑名中不存在的文件夾目錄創建出來
        }

        // 文件擴展名
        String fileExt = name.substring(name.lastIndexOf(".") + 1).toLowerCase();

        // 文件的絕對路徑File
        File uploadFile = new File(uploadAbsolutePath + "/" + newName);

        try {
            // 將上傳的圖片二進制流保存爲文件
            FileCopyUtils.copy(request.getInputStream(), new FileOutputStream(uploadFile));
        } catch (IOException ioException) {
            logger.error("圖片保存到文件夾中出錯!", ioException);
        } catch (Exception e) {
            logger.error("文件沒有複製到指定的目錄下" ,e);
        }

此時後端就可以獲取前端上傳的圖片了,ios 機型(iPhone 6s)依然沒問題,安卓上傳png格式的圖片沒有任何問題,可是jpg依然沒法上傳。在後端的時候,打印 requestheaders,發現安卓機型上傳jpg圖片是會丟失 content-type,值爲空。結合 issues 中的判斷,也許是安卓機型在壓縮 jpg 格式圖片時出了問題,先解決再試試看!

第二步:加上androidpatch

根據官方說明,使用 webuploader.custom.js,其中將 runtime/html5/androidpatch.js 打包了進來。

而後在沒有修改任何代碼的狀況下,通過五個手機的測試,新老機型:華爲榮耀、魅藍、聯想等等的測試,安卓機能夠在微信中隨意上傳圖片了。這是個大坑啊!說明沒法上傳 jpg 格式圖片的緣由竟是壓縮 jpg 格式圖片的時候出錯,致使進度條卡死,上傳失敗。

總結使用心得

按照以上的總結,我想下一次我應該能再一次利用這一次的經驗解決微信上傳圖片的坑了~也懂得從requestheaders 中尋找 bug 發生的緣由,WebUploader 是個很優秀的開源插件,源碼也寫的頗有條理,清晰易讀,雖然我並無讀完。如今閱讀框架源碼是愈來愈輕鬆了,加油,下個目標是正在學習的 React.js

相關文章
相關標籤/搜索