uniapp 的 H5和微信小程序上传 base64 图片

1. 前言

在做订单评论的时候,需要图片上传,开始使用的 FormData 上传,但是后端一直获取不到上传的 file ,由于之前是使用 taro 开发的H5和微信小程序,所以对 uniapp 开发不是很熟悉,使用了创建 input 标签,然后设置属性,来获取图片,最后提交上传。结果发现还是提交不成功,最后发现在 taro 那边使用的将图片转换为 base64 ,然后再提交上传,就按照这个思路实现。

2. H5 将选择图片转 base64

2.1 使用 XMLHttpRequest 和 FileReader 实现图片转 base64

ini 复制代码
// H5端获取图片的base64
function getWebBase64 (filePath) {
  return new Promise((resolve, reject) => { 
    let xhr = new XMLHttpRequest();
		xhr.open('GET', filePath, true);
		xhr.responseType = 'blob';
		xhr.onload = function() {
			if (this.status === 200) {
				let fileReader = new FileReader();
				fileReader.onload = function(e) {
					resolve(e.target.result);
				}
				fileReader.onerror = reject;
				fileReader.readAsDataURL(this.response);
			}
		}
		xhr.onerror = reject;
		xhr.send();
  })
}

2.2 使用 fetch 和 FileReader 实现图片转 base64

javascript 复制代码
// H5端获取图片的base64
function getWebBase64 (filePath) {
  return new Promise((resolve, reject) => { 
    fetch(filePath).then(fetchRes => {
      return fetchRes.blob()
    }).then(data => {
      let fr = new FileReader()
      fr.onload = function(){
        resolve(fr.result)
      }
      fr.readAsDataURL(data)
    })
  })
}

2.3 使用 canvas 实现图片转 base64

ini 复制代码
// H5端获取图片的base64
function getWebBase64 (filePath) {
  return new Promise((resolve, reject) => { 
    let canvas = document.createElement('canvas');
		let ctx = canvas.getContext('2d');
		let img = new Image;
		img.onload = function() {
			canvas.width = img.width;
			canvas.height = img.height;
			ctx.drawImage(img, 0, 0);
			resolve(canvas.toDataURL());
			canvas.height = canvas.width = 0;
		}
		img.onerror = reject;
		img.src = filePath;
  })
}

3. 微信小程序 将选择图片转 base64

javascript 复制代码
// 微信小程序端获取图片的base64
function getWeappBase64 (filePath) {
  return new Promise((resolve, reject)=>{
    let fileManage = uni.getFileSystemManager();
    fileManage.readFile({
      filePath,
      encoding:'base64',
      success:(e)=>{
        resolve(`data:image/jpg;base64,${e.data}`)
      },
      fail: err => {
        reject(err)
      }
    })
  })
}

4. 判断环境调用不同的方法

scss 复制代码
//上传的图片转为base64
export function fileToBase64 (filePath) {
  // #ifdef MP-WEIXIN
  return getWeappBase64(filePath)
  // #endif
  // #ifdef H5
  return getWebBase64(filePath)
  // #endif
}

5. 组件实现

  1. HTML 实现上传图片的盒子;
  2. 引入文件转 base64 的方法,上边我们实现了方法的具体实现,直接复制就可以使用;
  3. 引入 axios 请求的上传接口, ev 发布订阅实现的仅当前次上传成功,才能进行下次上传;
  4. handleUploadImage 上传方法的实现:
  5. 定义一个上传事件 RUI_HANDLE_UPLOAD_IMAGE_EVENT,只有提交成功,事件才会清除,允许下次提交;
  6. 事件回调函数会返回一个 done 函数,用于在上传成功或者失败的时候告诉RUI_HANDLE_UPLOAD_IMAGE_EVENT提交完成,清除当前次订阅;
  7. uni.chooseImage 选择一张图片,限制一张一张上传,是防止上传图片过大,转换 base64 导致提交数据过大,处理慢的问题;
  8. 获取选择图片的图片信息 uni.getImageInfo;
  9. 通过 fileToBase64 将图片转换为 base64;
  10. 调用 axios.uploadOssUploadString 上传图片,注意此处使用自己的上传接口;
  11. 通过 success 返回上传成功的结果;
  12. 最后执行 done 函数,可以进行下次提交订阅。
xml 复制代码
<template>
  <view 
    @click="handleUploadImage"
    class="rui-upload-image-component-content">
    <slot></slot>
  </view>
</template>
<script>
import { fileToBase64 } from './index';
import { axios, ev } from '@/utils';
export default {
  name: 'RuiUploadImage',
  methods: {
    handleUploadImage(){
      ev.once('RUI_HANDLE_UPLOAD_IMAGE_EVENT', done => {
        new Promise((resolve, reject) => {
          // 选择图片
          uni.chooseImage({
            count: 1,
            success: ({ tempFilePaths, tempFiles }) => {
							return resolve(tempFilePaths)
            },
            fail: reject
          })
        }).then(tempFilePaths => {
					return new Promise((resolve, reject) => {
            // 获取图片信息,获取图片base64
						uni.getImageInfo({
						  src: tempFilePaths[0],
						  success: ({ path }) => {
						    fileToBase64(path).then(base64 => {
						      resolve(base64)
						    }).catch(reject)
						  },
						  fail: reject
						})
					})
				}).then(file => {
          // 上传图片到oss
          return axios.uploadOssUploadString({ file, type: 0, level: 1})
        }).then(res => {
          // 返回id获取完整地址
          this.$emit('success', { ...res })
        }).finally(done);
      })
      
    }
  }
}
</script>

6. 使用

6.1 script 实现

javascript 复制代码
	import RuiUploadImage from '@/components/RuiUploadImage/index.vue';
	export default {
		components: {
			RuiUploadImage
		},
		data() {
			return {
				evaluateInfo: {
					order_id: '',
					order_service_score: 5,
					content: '',
					images: []
				},
				imageMap: new Map()
			}
		},
		methods: {
			// 选择上传的图片
			success(res){
				this.imageMap.set(res.file_id, res)
				this.evaluateInfo.images = [...this.imageMap.values()]
			},
			// 删除图片
			deleteImage(file_id){
				this.imageMap.delete(file_id)
				this.evaluateInfo.images = [...this.imageMap.values()]
			}
		}
	}

6.2 HTML 实现

ini 复制代码
<view class="upload-box-list rui-flex-ac">
	<view class="rui-flex-ac">
		<view class="rui-upload-handle-box rui-pr rui-mr15" 
			v-for="item in evaluateInfo.images" 
			:key="item.file_id">
			<image :src="icon.paySuccessClose" @click="deleteImage(item.file_id)" class="rui-delete-image rui-icon32"/>
			<image :src="item.url" class="rui-upload-handle-box"/>
		</view>
	</view>
	<RuiUploadImage @success="success" v-if="evaluateInfo.images.length < 3">
		<view class="rui-upload-handle-box rui-flex-cc">
			<view class="rui-fa">
				<view class="rui-flex-cc">
					<image :src="icon.evaluateUpload" class="rui-icon60"/>
				</view>
				<view class="rui-fs24 rui-color7 rui-mt10">添加图片</view>
			</view>
		</view>
	</RuiUploadImage>
</view>

7. 实现结果

8. 注意

  1. 由于该项目只需要H5和微信小程序,所以只做了H5和微信小程序的兼容,如果需要更加全面的转换 base64 建议使用插件市场的插件;
  2. 此处可能有人注意到我保存图片使用的是 Map 对象,然后展示的时候使用的数组,为什么要分开操作呢?是操作数组的删除会比较麻烦,我这里不管添加还是删除,直接操作 Map 对象,然后将 Map 的最新 value 列表给渲染列表,实现一样的操作,但是简化了我遍历的步骤,不用查找位置再删除,直接删除 Map 对应的 key ,然后获取最新的 value 列表就好。
相关推荐
Ratten3 小时前
uniapp的H5 在 UC 浏览器中返回上一页失效的解决方案
uni-app
Ratten4 小时前
使用 uniapp 扩展组件 uni-file-picker 实现视频和图片都可以预览展示
uni-app
Ratten4 小时前
在 HBuilderX 中使用 tailwindcss
uni-app
江湖行骗老中医16 小时前
uniapp 引入使用u-view 完整步骤,u-view 样式不生效
uni-app
雪芽蓝域zzs17 小时前
uniapp 页面favicon.ico文件不存在提示404问题解决
uni-app
一嘴一个橘子17 小时前
uniapp 顶部tab + 占满剩余高度的内容区域swiper
javascript·uni-app
睡美人的小仙女12717 小时前
在 UniApp 中,实现下拉刷新
uni-app
iOS阿玮20 小时前
江湖传闻谷歌比苹果严格多了,那么到底有多狠?
uni-app·app·apple
!win !1 天前
uni-app支付宝端彻底禁掉下拉刷新效果
前端·小程序·uni-app