微信小程序____上传图片(附后端代码)

引言

最近公司需要做个冲印类型的小程序,一阵徘徊后,才决定快速集成一个微型产品来。

正题

几乎每个程序都需要用到图片。
在小程序中我们可以通过image组件显示图片。

当然小程序也是可以上传图片的,参照微信小程序官方文档

step_1 选择图片

  • 通过wx.chooseImage(OBJECT)实现

官方示例代码:

wx.chooseImage({ 
  count: 1, // 默认9 
  sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
  sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
  success: function (res) { 
  // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
  var tempFilePaths = res.tempFilePaths } 
  })

图片最多只可以选择9张, 也可以通过拍摄照片实现,当选择完图片之后会获取到图片路径, 这个路径在本次启动期间有效。
如果需要保存就需要用wx.saveFile(OBJECT)

step_2 上传图片

  • 通过wx.uploadFile(OBJECT) 可以将本地资源文件上传到服务器。

原理就是客户端发起一个 HTTPS POST 请求,其中 content-typemultipart/form-data

官方示例代码

wx.chooseImage({
  success: function(res) {
    var tempFilePaths = res.tempFilePaths
    wx.uploadFile({
      url: 'http://example.weixin.qq.com/upload', //仅为示例,非真实的接口地址
      filePath: tempFilePaths[0],
      name: 'file',
      formData:{
        'user': 'test'
      },
      success: function(res){
        var data = res.data
        //do something
      }
    })
  }
})

示例代码

看完了文档, 写一个上传图片就没有那么麻烦了,下面是真实场景的代码

import constant from '../../common/constant';
Page({
  data: {
    src: "../../image/photo.png",  //绑定image组件的src
     //略...
  },
  onLoad: function (options) {
      //略... 
  },
  uploadPhoto() {
    var that = this; 
    wx.chooseImage({
      count: 1, // 默认9
      sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
      sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
      success: function (res) {
        // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
        var tempFilePaths = res.tempFilePaths;
        upload(that, tempFilePaths);
      }
    })
  }
})

function upload(page, path) {
  wx.showToast({
    icon: "loading",
    title: "正在上传"
  }),
  wx.uploadFile({
    url: constant.SERVER_URL + "/FileUploadServlet",
    filePath: path[0], 
    name: 'file',
    header: { "Content-Type": "multipart/form-data" },
    formData: {
      //和服务器约定的token, 一般也可以放在header中
      'session_token': wx.getStorageSync('session_token')
    },
    success: function (res) {
      console.log(res);
      if (res.statusCode != 200) { 
        wx.showModal({
          title: '提示',
          content: '上传失败',
          showCancel: false
        })
        return;
      }
      var data = res.data
      page.setData({  //上传成功修改显示头像
        src: path[0]
      })
    },
    fail: function (e) {
      console.log(e);
      wx.showModal({
        title: '提示',
        content: '上传失败',
        showCancel: false
      })
    },
    complete: function () {
      wx.hideToast();  //隐藏Toast
    }
  })
}
后端代码

后端是用java写的,一开始的时候,后端开始用了一些框架接收上传的图片,出现了各种问题,后来使用了纯粹的Servlet就没有了问题, 把代码贴出来省的以后麻烦了。

注意: 代码使用了公司内部的框架,建议修改后再使用

public class FileUploadServlet extends HttpServlet {
    
    private static final long serialVersionUID = 1L;
    private static Logger logger = LoggerFactory.getLogger(FileUploadServlet.class);
    
    public FileUploadServlet() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        JsonMessage<Object> message = new JsonMessage<Object>();
        EOSResponse eosResponse = null;
        String sessionToken = null;
        FileItem file = null;
        InputStream in = null;
        ByteArrayOutputStream swapStream1 = null;
        try {
            request.setCharacterEncoding("UTF-8"); 
            
            //1、创建一个DiskFileItemFactory工厂  
            DiskFileItemFactory factory = new DiskFileItemFactory();  
            //2、创建一个文件上传解析器  
            ServletFileUpload upload = new ServletFileUpload(factory);
            
            //解决上传文件名的中文乱码  
            upload.setHeaderEncoding("UTF-8");   
            // 1. 得到 FileItem 的集合 items  
            List<FileItem> items = upload.parseRequest(request);
            logger.info("items:{}", items.size());
            // 2. 遍历 items:  
            for (FileItem item : items) {  
                String name = item.getFieldName();  
                logger.info("fieldName:{}", name);
                // 若是一个一般的表单域, 打印信息  
                if (item.isFormField()) {  
                    String value = item.getString("utf-8");  
                    if("session_token".equals(name)){
                        sessionToken = value;
                    }
                }else {
                    if("file".equals(name)){
                        file = item;
                    }
                }  
            }
            //session校验
            if(StringUtils.isEmpty(sessionToken)){
                message.setStatus(StatusCodeConstant.SESSION_TOKEN_TIME_OUT);
                message.setErrorMsg(StatusCodeConstant.SESSION_TOKEN_TIME_OUT_MSG);
            }
            String userId = RedisUtils.hget(sessionToken,"userId");
            logger.info("userId:{}", userId);
            if(StringUtils.isEmpty(userId)){
                message.setStatus(StatusCodeConstant.SESSION_TOKEN_TIME_OUT);
                message.setErrorMsg(StatusCodeConstant.SESSION_TOKEN_TIME_OUT_MSG);
            }
            //上传文件
            if(file == null){
            }else{
                swapStream1 = new ByteArrayOutputStream();
                
                in = file.getInputStream();
                byte[] buff = new byte[1024];
                int rc = 0;
                while ((rc = in.read(buff)) > 0) {
                    swapStream1.write(buff, 0, rc);
                }
                
                Usr usr = new Usr();
                usr.setObjectId(Integer.parseInt(userId));
                
                final byte[] bytes = swapStream1.toByteArray();
                
                eosResponse= ServerProxy.getSharedInstance().saveHeadPortrait(usr, new RequestOperation() {
                    
                    @Override
                    public void addValueToRequest(EOSRequest request) {
                        request.addMedia("head_icon_media", new EOSMediaData(EOSMediaData.MEDIA_TYPE_IMAGE_JPEG, bytes));
                    }
                });
                
                // 请求成功的场合
                if (eosResponse.getCode() == 0) {
                    message.setStatus(ConstantUnit.SUCCESS);
                } else {
                    message.setStatus(String.valueOf(eosResponse.getCode()));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            try {
                if(swapStream1 != null){
                    swapStream1.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(in != null){
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        PrintWriter out = response.getWriter();  
        out.write(JSONObject.toJSONString(message));  
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

小程序前端效果____仿朋友圈的照片上传。

很粗糙、很简陋,请勿怪。

先上图:

首先说明一下我的前端基本诉求:

  • 一行明确指定图片显示个数
  • 可从照片图库一次多选若干张照片并显示在前端
  • 照片与照片之间有明显的间隔
  • 达到一定数量照片时将添加按钮隐藏
  • 添加按钮加上一些样式

index.wxml

<view class='uploadContainer'>
  <view wx:for="{{image}} wx:key="*this"">
    <image src='{{image[index]}}' bindload='imageLoad' style='width:{{pic_width}}px;height:{{pic_height}}px;margin:{{margin_length}}px' />
    <button bindtap='deleteImg' data-num='{{index}}' class='deleteBtn' style='width:{{pic_width}}px;margin:{{margin_length}}px'>删除</button>
  </view>
  <image src='../../icons/add.png' bindtap='uploadImg' class='addimg' style='display:{{img_button}};width:{{pic_width-2}}px;height:{{pic_height-2}}px ;margin:{{margin_length}}px' />
</view>

index.wxss

.uploadContainer {
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  flex-wrap: wrap;
}
.addimg {
  border-style: inset;
  border-width: 1px;
  border-color: #ddd;
}

index.js

//index.js
//获取应用实例
var app = getApp()
var px2rpx = 2, windowWidth = 375;
Page({
  /**
   * 页面的初始数据
   */
  data: {
    pic_width: 300,    //初始化值
    pic_height: 300,  //初始化值

    img_button: 'inline-block',
    image: [],
    msg: '',
    margin_length: 3,//显示图片的外边距margin值(单位:px像素)
  },

  //事件处理函数
  onLoad: function () {
    console.log('onLoad');
    wx.getSystemInfo({
      success: function (res) {
        windowWidth = res.windowWidth;
        px2rpx = 750 / windowWidth;
      }
    })

    this.setData({
      pic_width: (windowWidth - 8 * this.data.margin_length) / 4,
      pic_height: (windowWidth - 8 * this.data.margin_length) / 4
    })
  },

  onPullDownRefresh: function () {
    wx.stopPullDownRefresh()
  },

  onShareAppMessage: function () {
    return {
      title: 'xx公司',
      path: 'pages/index/index'
    }
  },

  uploadImg: function () {
    var that = this;
    var image = this.data.image;
//设定最终一起上传照片的最大数量
    if (this.data.image.length < 101) {
      wx.chooseImage({
        count: 9, // 默认9        
        sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有        
        sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有        
        success: function (res) {
          wx.uploadFile({
            url: app.appUrl.url + 'upload',//这个方法就是后台处理上传的方法            
            filePath: res.tempFilePaths[0], //获取到上传的图片            
            name: 'file',
            success: function (info) {
              console.log(info);//info.data就是上传成功的图片名称 您可以在wxml里面搞一个隐藏域存储起来,在上面Submit提交里拼装一块提交出去  
            }
          })
        },
        complete: function (res) {
          // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
          console.log(res.tempFilePaths);
          //判空处理,不能使用res.tempFilePaths.length方法,会报空指针
          //解决了取消选择照片时会将空照片上传的问题
          if(res.tempFilePaths!=null){
          //语法:concat的用法,concat之后返回的是一个新的对象,因此要赋值给image
          image=image.concat(res.tempFilePaths);
          that.setData({ image: image })
          //当有100张照片上传时,将上传图片的按钮隐藏掉
          if (that.data.image.length == 100) {
            that.setData({
              img_button: 'none',
            })
          }
          }
        }
      })
    }
  },
  deleteImg: function (e) {
    var image = this.data.image;
    image.splice(e.currentTarget.dataset.num, 1);
    this.setData({
      image: image,
      img_button: 'inline-block',
    })
  },
})

小程序前端效果____自定义需求。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容