• 文件上传那些事儿


    最近M端项目中涉及到图片上传功能,现把项目中遇到的一些问题及解决办法分享如下,与各位共同探讨:

    PS:文章已同步至个人博客

    一、相关需求:

    1. 客户端上限10M
    2. 服务器端上限2M
    3. 文件过滤
    4. 显示上传进度
    5. 异步上传
    6. 多文件上传

    二、需求分析:

    1. 实现2,可使用canvas在前端实现压缩(base64);
    2. 实1、3,可采用file.size及/image/.test(file.type)过滤;
    3. 实现4,使用XHR2实现上传,添加进度时间监控,xhr.upload.addEventListener(“progress”, uploadProgress, false);
    4. 实现5,使用XHR2实现上传;
    5. 多文件上传,单文件循环上传即可,但是兼容进度时,需单文件各自监控;

    三、需求实现:

    实现一:二进制方式上传
    需求1、3、4、5、6皆可实现,但是服务器端上传2M,使用canvas方式压缩后生成的是base64,若使用此方式上传,必须把base64转换成二进制流,GitHub上也有相关文章把base64转换成二进制流的,使用xhr.sendAsBinary()发送二进制流,参考此文,测了一部分常见机型,可以实现,具体是否可以在项目中使用还有待论证。二进制上传实现部分代码仅供参考:

    var uploadFile = function(fileid, file) {
      var xhr = new XMLHttpRequest(),
          fd = new FormData,
          url = "/picture/upload",
          // 上传进度
          uploadProgress = function(evt) {
            if (evt.lengthComputable) {
              var percent = (evt.loaded * 100 / evt.total).toFixed(1);
              // show percent
            }else {
              console.log('unable to compute');
            }
          },
          uploadFailed = function(evt) {
            // 上传失败
          },
          uploadCanceled = function(evt) {
            // 取消上传或网络连接断开!
          };
    
      xhr.upload.addEventListener("progress", uploadProgress, false);
      xhr.addEventListener("error", uploadFailed, false);
      xhr.addEventListener("abort", uploadCanceled, false);
      xhr.open("POST", url , true);
      xhr.onreadystatechange = function(e) {
        if (xhr.readyState == 4) {
          if (xhr.status === 200) {
            var data = xhr.response;
            if (data != 0) {
              // 上传失败
            } else {
              // 上传成功
            }
          } else {
            // 上传失败
          }
        }
      };
    
      fd.append(fileid, file);
      xhr.send(fd);
    };

    实现二:base64上传

    需求1、2、3、5、6皆可实现,实现此种方式即基本的Get上传,但是无法实时监控上传文件进度,需求4无法实现。

    实现三:二进制+base64
    即上述两种方案的综合。也可参考此文移动端Web上传图片实践中的实例。

    四、问题总结:

    M端浏览器各异,支持情况各异,现总结如下:
    a) 部分酷派机型浏览器(微信、UC、QQ、百度),中兴自带浏览器不支持input[type=file];
    解决方式:放弃

    b) Adroid机型,不同浏览器对input[type=file]支持不同,有的没有图库选项,有的没有相机选项。主要表现为小米、酷派部分机型的微信自带浏览器。
    解决方式:input[type=file]添加accept=’image/*’属性,可实现某些adriod机型不出现文档选项。

    c) 上传文件时,出现图片自动旋转的问题
    解决方式:实现开源插件CanvasResize中exif.js来纠正,实现此插件可解决压缩、纠正图片旋转,但Adroid上UC浏览器中会出现下图问题:(国外人写的插件哪会管国内浏览器死活!)
    uc-bug
    最后采用的腾讯的一款压缩方案,解决了UC浏览器的问题。

    d) 使用压缩插件时需注意,PNG图片压缩时往往会偏大,可把压缩成image/jpeg格式;
    var cvs = document.createElement(‘canvas’);
    var ctx = cvs.getContext(“2d”).drawImage(source_img_obj, 0, 0);
    var newImageData = cvs.toDataURL(‘image/jpeg’, quality/100);

    f) 因浏览器对input[type=file]显示风格各异,项目使用label的for指向input[type=file]的id,并设置input{display:none};在Adroid部分浏览器上点击无反应;
    解决方式:设置input{position: absolute; top: -99em;}来隐藏。

    g) 在部分Adroid支持input[type=file]的浏览器中,当使用/image/.test(file.type)时,选择图片文件会返回false。使用JSON.stringify(file)分析后发现,是file对象中的name字段中没有包含后缀,同时type字段为空,使用this.value获取路径中也没有包含后缀。因此过滤出现问题。
    如下结果:

    1 {“webkitRelativePath”:””,”lastModified”:1433304214000,”lastModifiedDate”:”2015-06-03T04:03:34.000Z”,“name”:”fanmian”,”type”:””,”size”:2273852}

    正常结果如下:

    1 {“webkitRelativePath”:””,”lastModified”:1433304214000,”lastModifiedDate”:”2015-06-03T04:03:34.000Z”,“name”:”fanmian.png”,”type”:”image/png”,”size”:2273852}

    解决方式:放开/image/.test(file.type)过滤,在压缩时,抛出错误过滤。

    h) html5上传文件,Firefox支持重复选择同一文件,其它浏览器不支持
    解决方式:每次选择文件后给input[type=file]赋值空。

    文中不妥之处,欢迎批评指正!

    五、参考链接:

    Html5 File Upload with Progress
    移动端Web上传图片实践

    图片压缩成base64,采用二进流上传

  • 相关阅读:
    WebService 通过POST方式访问时候,因 URL 意外地以“/方法名”结束,请求格式无法识别 解决办法
    SQL Server 触发器
    JS数据类型转换
    .net注册到IIS
    SQL Server 常用sql操作语句
    浅解DLL
    有关注册表API函数
    [原]惜 时
    图解双机共享ADSL上网
    如何在C#中使用全局鼠标、键盘Hook
  • 原文地址:https://www.cnblogs.com/cuew1987/p/4657451.html
Copyright © 2020-2023  润新知