基于vue+axios+lrz.js微信端图片压缩上传方法

业务场景

微信端项目是基于Vux + Axios构建的,关于图片上传的业务场景有以下几点需求:

1、单张图片上传(如个人头像,实名认证等业务)

2、多张图片上传(如某类工单记录)

3、上传图片时期望能按指定尺寸压缩处理

4、上传图片可以从相册中选择或者直接拍照

遇到的坑

采用微信JSSDK上传图片

在之前开发的项目中(mui + jquery),有使用过微信JSSDK的接口上传图片,本想应该能快速迁移至此项目。事实证明编程没有简单的事:

1、按指定尺寸压缩图片

JSSDK提供的接口wx.chooseImage 是不能指定图片压缩尺寸的,只能在后端的接口通过localId获取图片时,再转换成指定的尺寸。

2、微信JSSDK的接口权限验证

只要是单页面应用项目,微信JSSDK注入权限验证都会有这个坑,而这个与路由模式(hash 或 history)也有关联。有关此坑, 后续会再次写文总结。参考解决方案[微信JSSDK] 解决SDK注入权限验证 安卓正常,IOS出现config fail

经过权衡考虑网页可能需要在微信以外的浏览器上也能上传文件,顾后来放弃了采用微信JSSDK接口上传图片的方式。

android版微信,input onchange事件不触发

这个坑,圈内有很多人踩过了。在PC端测试是正常的,发布之后,微信端上传时能选择文件,但之后没有任何效果。日志跟踪,后台的api都未调用,由此判断是input的onchange事件未被触发。

解决方案, 更改input的 accept属性:

<input ref="file" type="file" accept="image/jpeg,image/png" @change="selectImgs" />

将以上代码更改为:

<input ref="file" type="file" accept="image/*" @change="selectImgs" />

如果不允许从相册中选择,只能拍照,增加capture="camera":

<input ref="file" type="file" accept="image/*" capture="camera" @change="selectImgs" />

(注:如果场景支持从相册选择或拍照,测试发现某些机型拍照后返回到了主页。哈哈,也有可能是其他因素引起的问题,未做深究了)

使用Lrz.js压缩图片

目前手机拍照的图片文件大小一般在3-4M,如果在上传时不做压缩处理会相当浪费流量并且占用服务器的存储空间(期望上传原图的另做讨论)。如果能够在前端压缩处理,那肯定是最理想的方案。而 lrz.js 则提供了前端图片文件的压缩方案,并且可以指定尺寸压缩。实测:3M左右的图片文件,按宽度450px尺寸压缩上传后的文件大小在500kb左右,上传时间2s以内。

其核心源码,如下:

selectImgs () {
 let file = this.$refs.file.files[0]
 lrz(file, { width: 450, fieldName: 'file' }).then((rst) => {
  var xhr = new XMLHttpRequest()
  xhr.open('POST', 'http://xxx.com/upload')

  xhr.onload = () => {
   if (xhr.status === 200 || xhr.status === 304) {
    // 无论后端抛出何种错误,都会走这里
    try {
     // 如果后端跑异常,则能解析成功, 否则解析不成功
     let resp = JSON.parse(xhr.responseText)
     console.log('response: ', resp)
    } catch (e) {
     this.imageUrl = xhr.responseText
    }
   }
  }

  // 添加参数
  rst.formData.append('folder', 'wxAvatar') // 保存的文件夹
  rst.formData.append('base64', rst.base64)
  // 触发上传
  xhr.send(rst.formData)

  return rst
 })
}

单个图片上传组件完整代码,如下(注: icon图标使用的是svg-icon组件):

<template>
 <div class="imgUploader">
  <section v-if="imageUrl"
       class="file-item ">
   <img :src="imageUrl"
      alt="">
   <span class="file-remove"
      @click="remove()">+</span>
  </section>
  <section v-else
       class="file-item">
   <div class="add">
    <svg-icon v-if="!text"
         class="icon"
         icon-class="plus" />
    <span v-if="text"
       class="text">{{text}}</span>
    <input type="file"
        accept="image/*"
        @change="selectImgs"
        ref="file">
   </div>
  </section>
 </div>
</template>

<script>
import lrz from 'lrz'
export default {
 props: {
  text: String,
  // 压缩尺寸,默认宽度为450px
  size: {
   type: Number,
   default: 450
  }
 },
 data () {
  return {
   img: {
    name: '',
    src: ''
   },
   uploadUrl: 'http://ff-ff.xxx.cn/UploaderV2/Base64FileUpload',
   imageUrl: ''
  }
 },
 watch: {
  imageUrl (val, oldVal) {
   this.$emit('input', val)
  },
  value (val) {
   this.imageUrl = val
  }
 },
 mounted () {
  this.imageUrl = this.value
 },
 methods: {
  // 选择图片
  selectImgs () {
   let file = this.$refs.file.files[0]
   lrz(file, { width: this.size, fieldName: 'file' }).then((rst) => {
    var xhr = new XMLHttpRequest()
    xhr.open('POST', this.uploadUrl)

    xhr.onload = () => {
     if (xhr.status === 200 || xhr.status === 304) {
      // 无论后端抛出何种错误,都会走这里
      try {
       // 如果后端跑异常,则能解析成功, 否则解析不成功
       let resp = JSON.parse(xhr.responseText)
       console.log('response: ', resp)
      } catch (e) {
       this.imageUrl = xhr.responseText
      }
     }
    }

    // 添加参数
    rst.formData.append('folder', this.folder) // 保存的文件夹
    rst.formData.append('base64', rst.base64)
    // 触发上传
    xhr.send(rst.formData)

    return rst
   })
  },
  // 移除图片
  remove () {
   this.imageUrl = ''
  }
 }
}
</script>

<style lang="less" scoped>
.imgUploader {
 margin-top: 0.5rem;
 .file-item {
  float: left;
  position: relative;
  width: 100px;
  text-align: center;
  left: 2rem;
  img {
   width: 100px;
   height: 100px;
   border: 1px solid #ececec;
  }
  .file-remove {
   position: absolute;
   right: 0px;
   top: 4px;
   width: 14px;
   height: 14px;
   color: white;
   cursor: pointer;
   line-height: 12px;
   border-radius: 100%;
   transform: rotate(45deg);
   background: rgba(0, 0, 0, 0.5);
  }

  &:hover .file-remove {
   display: inline;
  }
  .file-name {
   margin: 0;
   height: 40px;
   word-break: break-all;
   font-size: 14px;
   overflow: hidden;
   text-overflow: ellipsis;
   display: -webkit-box;
   -webkit-line-clamp: 2;
   -webkit-box-orient: vertical;
  }
 }
 .add {
  width: 100px;
  height: 100px;
  float: left;
  text-align: center;
  line-height: 100px;
  font-size: 30px;
  cursor: pointer;
  border: 1px dashed #40c2da;
  color: #40c2da;
  position: relative;
  background: #ffffff;
  .icon {
   font-size: 1.4rem;
   color: #7dd2d9;
   vertical-align: -0.25rem;
  }
  .text {
   font-size: 1.2rem;
   color: #7dd2d9;
   vertical-align: 0.25rem;
  }
 }
}
input[type="file"] {
 position: absolute;
 left: 0;
 top: 0;
 width: 100%;
 height: 100%;
 border: 1px solid #000;
 opacity: 0;
}
</style>

后端图片存储处理

后端api对图片的处理,是必不可少的环节,需要将前端提交过来的base64字符串转换成图片格式,并存放至指定的文件夹,接口返回图片的Url路径。各项目后端对图片的处理逻辑都不一致,以下方案仅供参考(我们使用asp.net MVC 构建了独立的文件存储站点)。

其核心源码,如下:

/// <summary>
/// 图片文件base64上传
/// </summary>
/// <param name="folder">对应文件夹位置</param>
/// <param name="base64">图片文件base64字符串</param>
/// <returns></returns>
public ActionResult Base64FileUpload(string folder, string base64)
{
  var context = System.Web.HttpContext.Current;
  context.Response.ClearContent();
  // 因为前端调用时,需要做跨域处理
  context.Response.AddHeader("Access-Control-Allow-Origin", "*");
  context.Response.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
  context.Response.AddHeader("Access-Control-Allow-Headers", "content-type");
  context.Response.AddHeader("Access-Control-Max-Age", "30");
  if (context.Request.HttpMethod.Equals("OPTIONS"))
  {
    return Content("");
  }

  var resultStr = base64.Substring(base64.IndexOf(",") + 1);//需要去掉头部信息,这很重要
  byte[] bytes = Convert.FromBase64String(resultStr);
  var fileName = Guid.NewGuid().ToString() + ".png";
  if (folder.IsEmpty()) folder = "folder";
  //本地上传
  string root = string.Format("/Resource/{0}/", folder);
  string virtualPath = root + fileName;
  string path = Server.MapPath("~" + virtualPath);
  //创建文件夹
  if (!Directory.Exists(Path.GetDirectoryName(path)))
  {
    Directory.CreateDirectory(Path.GetDirectoryName(path));
  }
  System.IO.MemoryStream ms = new System.IO.MemoryStream(bytes);//转换成无法调整大小的MemoryStream对象
  System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(ms);
  bitmap.Save(path, System.Drawing.Imaging.ImageFormat.Png);//保存到服务器路径
  ms.Close();//关闭当前流,并释放所有与之关联的资源
  return Content(Net.Url + virtualPath); //返回文件路径
}

结语

由于项目实际情况,上述的方案中还存在诸多未完善的点:

1、多张图片上传,还是采用的与单张图片相同的接口处理, 更为完善的方案是,前端的多图上传组件只绑定一个关联Id,即可通过实现上传和将图片列表查询展示(注:该功能在微信端未实现)。

2、后端图片上传的接口,未做严格的安全校验,更为完善的方案是,每个上传的场景,都应该限制文件类型,限制文件大小,以及文件数据来源校验(注: 如软件需要按二级等保标准测评,则后端接口会检测通不过)。

3、上传组件,未显示上传进度,体验性稍差。

正如前文所述,出于项目实际情况考虑,只是简单实现图片压缩上传功能,如要支持更多的场景,还得细细雕琢。

(0)

相关推荐

  • 移动端 Vue+Vant 的Uploader 实现上传、压缩、旋转图片功能

    面向百度开发 html <van-uploader :after-read="onRead" accept="image/*"> <img src="./icon_input_add.png" /> </van-uploader> js data() { return { files: { name: "", type: "" }, headerImage: null,

  • vue.js 实现图片本地预览 裁剪 压缩 上传功能

    以下代码涉及 Vue 2.0 及 ES6 语法. 目标 纯 javascrpit 实现,兼容ie9及以上浏览器,在本地做好文件格式.长宽.大小的检测,减少浏览器交互. 现实是残酷的,为了兼容Ie9 还是用上了 flash,第二篇来解释解释. 代码结构 <div id="wrap"> <label> 点我上传图片 <input type='file' @change="change" ref="input"> &

  • Vue.js 2.0 移动端拍照压缩图片上传预览功能

    在学习和使用Vue.js 2.0 的过程中遇到不少不一样的地方,本来移动端开发H5应用,准备将mui框架和Vue.js+vue-router+vuex 全家桶结合起来使用,但是在拍照上传的实现过程中遇到了无法调用plus的H5+接口的问题,所以最后拍照上传功能还是使用input file方式里解决的.但是内心还是不甘心的,由于项目进度推进,迭代版本,所以不得不放弃,后续可能我将此功能使用调用H5+接口实现. 首先我来讲我实现这个拍照预览压缩上传的思路,准确的说应该是拍照或选择图片压缩之后预览及上

  • vue2实现移动端上传、预览、压缩图片解决拍照旋转问题

    因为最近遇到个移动端上传头像的需求,上传到后台的数据是base64位,其中为了提高用户体验,把比较大的图片用canvas进行压缩之后再进行上传.在移动端调用拍照功能时,会发生图片旋转,为了解决这个问题引入了exif去判断拍照时的信息再去处理图片,这是个很好的插件.关于exif.js可以去他的GitHub上了解,这边直接 npm install exif-js --save   安装,然后import一下就可以使用了.以下就是源码,可以直接使用. <template> <div> &

  • vue实现压缩图片预览并上传功能(promise封装)

    本文实例为大家分享了vue实现压缩图片预览并上传的具体代码,供大家参考,具体内容如下 主要用到filereader.canvas 以及 formdata 这三个h5的api 过程大致分为三步: 用户使用input file上传图片的时候,用filereader读取用户上传的图片数据(base64格式) 把图片数据传入img对象,然后将img绘制到canvas上,再调用canvas.toDataURL对图片进行压缩 获取到压缩后的base64格式图片数据,转成二进制塞入formdata,再通过Xm

  • vuejs开发组件分享之H5图片上传、压缩及拍照旋转的问题处理

    一.前言 三年.net开发转前端已经四个月了,前端主要用webpack+vue,由于后端转过来的,前端不够系统,希望分享下开发心得与园友一起学习. 图片的上传之前都是用的插件(ajaxupload),或者传统上传图片的方式,各有利弊:插件的问题是依赖jq并且会使系统比较臃肿,还有传统的web开发模式 前后端偶尔在一起及对用户体验要求低,现在公司采用webpack+vue+restfullApi开发模式 前后端完全分离,遵从高内聚,低偶尔的原则,开发人员各司其职,一则提升开发效率(从长期来看,短期

  • Vue.js 2.0 移动端拍照压缩图片预览及上传实例

    在学习和使用Vue.js 2.0 的过程中遇到不少不一样的地方,本来移动端开发H5应用,准备将mui框架和Vue.js+vue-router+vuex 全家桶结合起来使用,但是在拍照上传的实现过程中遇到了无法调用plus的H5+接口的问题,所以最后拍照上传功能还是使用input file方式里解决的.但是内心还是不甘心的,由于项目进度推进,迭代版本,所以不得不放弃,后续可能我将此功能使用调用H5+接口实现. 首先我来讲我实现这个拍照预览压缩上传的思路,准确的说应该是拍照或选择图片压缩之后预览及上

  • 基于vue+axios+lrz.js微信端图片压缩上传方法

    业务场景 微信端项目是基于Vux + Axios构建的,关于图片上传的业务场景有以下几点需求: 1.单张图片上传(如个人头像,实名认证等业务) 2.多张图片上传(如某类工单记录) 3.上传图片时期望能按指定尺寸压缩处理 4.上传图片可以从相册中选择或者直接拍照 遇到的坑 采用微信JSSDK上传图片 在之前开发的项目中(mui + jquery),有使用过微信JSSDK的接口上传图片,本想应该能快速迁移至此项目.事实证明编程没有简单的事: 1.按指定尺寸压缩图片 JSSDK提供的接口wx.choo

  • js移动端图片压缩上传功能

    移动端图片压缩上传功能如何实现? 做移动端开发的时候,form里面的file后台经常获取不到,用foemdata也拿不到 找到了一个formdata的脚本 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, initial-scale=1.0, maximum-

  • spring mvc+localResizeIMG实现HTML5端图片压缩上传

    最近在做一个移动端HTML5的应用,使用到了上传功能,起初使用传统的上传方式上传手机拍照的照片,由于手机拍照出来的照片一般都是好几MB,所以上传速度是非常慢的. 在网上找了很久找到了localResizeIMG压缩框架,感觉非常的实用,所以在此分享给大家. 第一步:下载localResizeIMG localResizeIMG放在github中的,地址是:https://github.com/think2011/localResizeIMG. 第二步:在web工程中导入localResizeIM

  • 移动前端图片压缩上传的实例

    摘要:之前在做一个小游戏平台项目,有个"用户中心"模块,就涉及到了头像上传的功能.在做移动端图片上传的时候,传的都是手机本地图片,而本地图片一般都相对比较大,拿现在的智能手机来说,平时拍很多图片都是两三兆的,如果直接这样上传,那图片就太大了,如果用户用的是移动流量,完全把图片上传显然不是一个好办法.所以上传之前进行压缩处理是必要的,在网上找了很多资料之后,尝试了很多方法,遇到了很多坑,比如安卓能够成功压缩上传图片,在ios上却上传不了,折腾了很久才发现ios的坑.一下这种已经进过实践证

  • H5移动端图片压缩上传开发流程

    H5活动已十分普遍,其中一种形式是让用户上传图片进行参与.移动端上传图片,用户一般都是上传手机相册中的图片,而现在手机的拍摄质量越来越高,一般单张照片的尺寸都在3M左右.若直接上传,十分耗流量,并且体验效果也不佳.因此需要在上传之前,先进行本地压缩. 接下来总结在h5活动的开发中图片压缩上传的功能,并标记其中踩过的几个坑,分享给大家: 小白区必看 对于移动端图片上传毫无概念的话,需要补充FileReader.Blob.FormData三个概念. 1.FileReader 定义 使用FileRea

  • vue实现移动端图片裁剪上传功能

    本文实例为大家分享了vue移动端图片裁剪上传的具体代码,供大家参考,具体内容如下 1. 安装cropperjs依赖库 npm install cropperjs 2. 编写组件SimpleCropper.vue <template> <div class="v-simple-cropper"> <slot> <button @click="upload">上传图片</button> </slot>

  • Cropper.js 实现裁剪图片并上传(PC端)

    由于之前做项目的时候有需求是需要实现裁剪图片来做头像并上传到服务器,所以上网查询了很多资料,也试用了许多案例,发现cropper插件裁剪是比较完善的,所以结合之前的使用情况,编写了此案例.本案例是参考cropper站点实例,进行修改简化. option相关参数说明: viewMode 显示模式 Type: Number Default: 0 Options: 0: the crop box is just within the container 裁剪框只能在 1内移动 1: the crop

  • Vue使用canvas实现图片压缩上传

    本文实例为大家分享了Vue使用canvas实现图片压缩上传的具体代码,供大家参考,具体内容如下 场景:如用户头像等 对于大尺寸图片的上传,在前端进行压缩除了省流量外,最大的意义是极大的提高了用户体验. 两方面: 1.由于上传图片尺寸比较小,因此上传速度会比较快,交互会更加流畅,同时大大降低了网络异常导致上传失败风险. 2.很多网站的图片上传功能都会对图片的大小进行限制,尤其是头像上传,限制5M或者2M以内是非常常见的(但是我用单反拍了个头像,照片超过2M很正常,要对图片进行处理才能上传).如果可

  • 推荐三款不错的图片压缩上传插件(webuploader、localResizeIMG4、LUploader)

    涉及到网页图片的交互,少不了图片的压缩上传,相关的插件有很多,相信大家都有用过,这里我就推荐三款,至于好处就仁者见仁喽: 1.名气最高的WebUploader,由Baidu FEX 团队开发,以H5为主,FLASH为辅,兼容 IE6+,iOS 6+, android 4+,采用大文件分片并发上传,极大的提高了文件上传效率,看了官方文档就知道,能满足你所需要的所有功能,一言以蔽之,大而全:至于缺点,大概就是插件体积太大了,自带样式文件,而且还要依赖jquery类库.详细的教程网上俯拾即是,这里我就

  • iOS实现图片压缩的两种方法及图片压缩上传功能

    两种压缩图片的方法:压缩图片质量(Quality),压缩图片尺寸(Size). 压缩图片质量 NSData *data = UIImageJPEGRepresentation(image, compression); UIImage *resultImage = [UIImage imageWithData:data]; 通过 UIImage 和 NSData 的相互转化,减小 JPEG 图片的质量来压缩图片.UIImageJPEGRepresentation:: 第二个参数 compressi

随机推荐