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

yizhihongxing

下面是对于“Vue.js 2.0 移动端拍照压缩图片上传预览功能”的完整攻略:

目标技术点

在实现 Vue.js 移动端拍照压缩图片上传预览功能时,需要掌握以下技能点:

  • Vue.js 2.x
  • 移动端兼容性问题的解决方案
  • HTML5 FormData
  • HTML5 File API
  • Image resize(图片压缩)

目标功能实现

实现以上技术点后,即可实现以下功能:

  • 通过移动端在线拍照,支持对图片进行任意尺寸的压缩;
  • 支持上传被压缩后的图片至服务器,通过Ajax获取到图片资源并显示;
  • 通过Vuetify库提供的卡片,实现简单美观的图片展示功能。

实现步骤

以下是Vue.js移动端拍照压缩图片上传预览功能的完整步骤:

第1步:库导入

使用Vue.js 2.x版本,我们可以简单地通过CDN导入Vue.js和Vuetify库的相关文件:

<!-- Vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<!-- Vuetify CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css">

<!-- Vuetify JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.js"></script>

第2步:创建Vue实例

在Vue实例中添加数据和方法等属性,用于实现移动端拍照、压缩图片上传和预览功能:

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data() {
    return {
      imgData: '', // 图片上传后的二进制数据
      imgURL: '', // 图片预览地址
      msg: '' // 错误提示信息
    };
  },
  methods: {
    // 拍照并压缩图片,最终上传数据
    uploadImage() {
      let that = this;
      let formData = new FormData();

      // 获取拍照数据
      let imgData = this.imgData.split(',')[1];

      // 压缩图片,最大宽度为800像素
      let imgBlob = this.dataURItoBlob(this.imgData);
      let imgFile = this.blobToFile(imgBlob, Date.now() + '.jpg');

      // 添加二进制图片文件到FormData中
      formData.append('imgFile', imgFile);

      // 发起Ajax上传请求,获取上传后的文件地址
      // 省略Ajax代码

      // 清空图片预览和二进制数据
      this.imgURL = '';
      this.imgData = '';
    },

    // 将DataURI转换为Blob类型的二进制数据
    dataURItoBlob(dataURI) {
      let byteString = atob(dataURI.split(',')[1]);
      let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
      let ab = new ArrayBuffer(byteString.length);
      let ia = new Uint8Array(ab);
      for (let i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
      }
      return new Blob([ab], { type: mimeString });
    },

    // 将Blob类型的文件数据转换为File类型
    // fileName:新生成的文件名称
    blobToFile(blob, fileName) {
      blob.lastModifiedDate = new Date();
      blob.name = fileName;
      return <File>blob;
    },

    // 获取拍照并压缩后的图片预览地址
    takePicture() {
      let that = this;

      // 相机初始化
      let video = document.querySelector('video');
      let canvas = document.querySelector('canvas');
      const constraints = {
        video: { width: 300, height: 300 }
      };

      // 获取摄像头数据
      navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
        video.srcObject = stream;
        // video.play();
      });

      // 拍照并压缩后显示图片预览
      setTimeout(function () {
        let ctx = canvas.getContext('2d');
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
        that.imgURL = canvas.toDataURL('image/png', 0.8);

        // 获取最终上传的数据,DataURI格式
        that.imgData = that.imgURL.replace(/^[^,]+,/, '');
      }, 500);
    },

    // 重置数据
    clearData() {
        this.imgURL = '';
        this.imgData = '';
        this.msg = '';
    }
  }
});

第3步:在Vue模板中使用Vuetify卡片显示并调用方法

接下来,在Vue的模板中添加HTML代码,实现调用上述方法来实现移动端拍照、压缩图片上传和预览功能。在代码中,我们将调用takePicture()来获取照片并压缩,然后通过Vuetify卡片将照片预览出来。点击“上传”按钮后,将调用uploadImage()来将压缩后的数据上传至服务器。

<div id="app">
  <v-app>
    <v-card width="450" height="450" class="mx-auto my-5">
      <v-img :src="imgURL" width="100%" height="300px" class="pt-4"></v-img>
      <v-input class="mt-4" label="照片名称" hint="请输入照片名称" v-model.trim="msg" clearable required></v-input>
      <v-card-actions>
        <v-btn color="primary" @click="takePicture">拍照</v-btn>
        <v-btn color="green" @click="uploadImage">上传</v-btn>
        <v-btn color="warning" @click="clearData">重置</v-btn>
      </v-card-actions>
    </v-card>
  </v-app>

  <!-- 拍照Canvas -->
  <div style="display:none;">
    <video autoplay></video>
    <canvas width="300" height="300"></canvas>
  </div>
</div>

第4步:实际应用示例

接下来,我们来看一下两个实际应用的示例:

示例1:与 Node.js 和 Express.js 配合

前端代码:

<!-- index.html -->

<div id="app">
  <v-app>
    <v-card width="450" height="450" class="mx-auto my-5">
      <v-img :src="imgURL" width="100%" height="300px" class="pt-4"></v-img>
      <v-input class="mt-4" label="照片名称" hint="请输入照片名称" v-model.trim="msg" clearable required></v-input>
      <v-card-actions>
        <v-btn color="primary" @click="takePicture">拍照</v-btn>
        <v-btn color="green" @click="uploadImage">上传</v-btn>
        <v-btn color="warning" @click="clearData">重置</v-btn>
      </v-card-actions>
    </v-card>
  </v-app>

  <!-- 拍照Canvas -->
  <div style="display:none;">
    <video autoplay></video>
    <canvas width="300" height="300"></canvas>
  </div>
</div>

<!-- Vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<!-- Vuetify CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css">

<!-- Vuetify JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.js"></script>

<!-- Axios JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

<!-- Vue.js 代码 -->
<script>

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data() {
    return {
      imgData: '', // 图片上传后的二进制数据
      imgURL: '', // 图片预览地址
      msg: '' // 错误提示信息
    };
  },
  methods: {
    // 拍照并压缩图片,最终上传数据
    uploadImage() {
      let that = this;

      if (this.msg === '') {
        this.$message({
          type: 'error',
          message: '请您输入照片名称'
        });
        return false;
      }

      if (this.imgData === '') {
        this.$message({
          type: 'error',
          message: '请您先拍摄照片'
        });
        return false;
      }

      let formData = new FormData();

      // 获取拍照数据
      let imgData = this.imgData.split(',')[1];

      // 压缩图片,最大宽度为800像素
      let imgBlob = this.dataURItoBlob(this.imgData);
      let imgFile = this.blobToFile(imgBlob, Date.now() + '.jpg');

      // 添加二进制图片文件到FormData中
      formData.append('imgFile', imgFile);

      // 通过Axios上传FormData中的数据至Node.js
      axios.post('/upload', formData, {
        headers: { 'Content-Type': 'multipart/form-data' }
      }).then((res) => {
        console.log(res);

        // 获取上传后的图片资源
        axios.get('/getImage/' + res.data.fileID, {
          responseType: 'arraybuffer'
        }).then((res) => {
          console.log(res);

          // 显示图片
          that.imgURL = 'data:image/png;base64,' + btoa(new Uint8Array(res.data).reduce((data, byte) => data + String.fromCharCode(byte), ''));

          that.$message({
            type: 'success',
            message: '图片上传成功!'
          });
        }).catch((err) => {
          console.log(err);
        });
      }).catch((err) => {
        console.log(err);
      });

      // 清空图片预览和二进制数据
      this.imgURL = '';
      this.imgData = '';
    },

    // 将DataURI转换为Blob类型的二进制数据
    dataURItoBlob(dataURI) {
      let byteString = atob(dataURI.split(',')[1]);
      let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
      let ab = new ArrayBuffer(byteString.length);
      let ia = new Uint8Array(ab);
      for (let i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
      }
      return new Blob([ab], { type: mimeString });
    },

    // 将Blob类型的文件数据转换为File类型
    // fileName:新生成的文件名称
    blobToFile(blob, fileName) {
      blob.lastModifiedDate = new Date();
      blob.name = fileName;
      return <File>blob;
    },

    // 获取拍照并压缩后的图片预览地址
    takePicture() {
      let that = this;

      // 相机初始化
      let video = document.querySelector('video');
      let canvas = document.querySelector('canvas');
      const constraints = {
        video: { width: 300, height: 300 }
      };

      // 获取摄像头数据
      navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
        video.srcObject = stream;
        // video.play();
      });

      // 拍照并压缩后显示图片预览
      setTimeout(function () {
        let ctx = canvas.getContext('2d');
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
        that.imgURL = canvas.toDataURL('image/png', 0.8);

        // 获取最终上传的数据,DataURI格式
        that.imgData = that.imgURL.replace(/^[^,]+,/, '');
      }, 500);
    },

    // 重置数据
    clearData() {
        this.imgURL = '';
        this.imgData = '';
        this.msg = '';
    }
  }
});

</script>

后台代码:

/* server.js */

const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const multer = require('multer');
const FileType = require('file-type');
const fse = require('fs-extra');

const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json({ limit: '100mb' }));
app.use(bodyParser.urlencoded({ limit: '100mb', extended: true }));
app.use(cors({ origin: true }));

// 定义文件上传路径
const uploadPath = './upload/';

// 定义文件上传中间件,限制文件大小为10Mb
const upload = multer({
  limits: { fileSize: 10000000 },
  storage: multer.diskStorage({
    destination: function (req, file, cb) {
      cb(null, uploadPath);
    },
    filename: function (req, file, cb) {
      cb(null, Date.now() + '.jpg'); // 把所有的文件都存储为jpg格式
    }
  })
});

// 定义获取图片资源
app.get('/getImage/:id', (req, res) => {
  const id = req.params.id;
  const filePath = uploadPath + id + '.jpg';

  res.sendFile(filePath);
});

// 定义上传图片
app.post('/upload', upload.single('imgFile'), (req, res) => {
  const { originalname, mimetype, size } = req.file;
  const index = req.file.filename.split('.')[0];
  const ext = req.file.filename.split('.')[1];

  // 如果上传的文件不是图片,则返回错误信息
  if (mimetype.indexOf('image') < 0) {
    return res.status(400).json({ error: '仅支持上传图片数据!' });
  }

  // 如果上传的文件超过大小限制,则返回错误信息
  if (size > 10000000) {
    return res.status(400).json({ error: '上传文件不能超过10MB!' });
  }

  // 返回上传后的文件编号
  return res.json({ fileID: index });
});

// 监听端口
app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

在后台代码中,上传了所有的jpg格式的文件,您可以根据自己实际的项目需求来变更配置。

示例2:与PHP后端配合

前端代码:

```html






拍照
上传
重置







  • 浅谈小程序 setData学问多

    下面我将为你详细讲解浅谈小程序 setData 学问多的攻略。 什么是小程序 setData 小程序 setData 是小程序中用来动态更新页面数据的 API,用于更新小程序页面的数据及视图状态。通过调用 setData 方法,使页面得以响应用户的交互,实现数据的绑定,达到动态更新小程序页面的效果。 setData 的使用方法 setData 方法中接受一个…

    Vue 2023年5月27日
    00
  • vue本地模拟服务器请求mock数据的方法详解

    Vue本地模拟服务器请求Mock数据的方法详解 在开发Vue项目的过程中,我们可能需要在本地预览和测试一些接口。但是在实际的开发中,如果后端接口还没有开发完毕,我们就无法进行测试了。这时候,模拟服务器请求Mock数据就显得尤为重要。本文将详细讲解Vue本地模拟服务器请求Mock数据的方法。 1. 创建Mock数据 首先需要创建Mock数据。在项目的src目录…

    Vue 2023年5月28日
    00
  • vue如何定义全局变量和全局方法实例代码

    下面我将详细讲解Vue如何定义全局变量和全局方法的实例代码。 定义全局变量 在Vue中,定义全局变量可以通过Vue的原型挂载属性的方式,以下是具体实现步骤: 首先在Vue实例化之前,通过Vue的原型注册全局属性: Vue.prototype.$globalVariable = ‘global_variable’; 这个例子中注册了一个名为$globalVar…

    Vue 2023年5月28日
    00
  • Mpvue中使用Vant Weapp组件库的方法步骤

    使用Vant Weapp组件库的方法步骤: 安装Vant Weapp 在cmd中进入mpvue项目根目录,执行以下命令进行安装: npm install vant-weapp -S –production 在 App.vue 中引入 Vant Weapp 在 App.vue 的 script 标签中如下添加: import Vant from ‘vant-…

    Vue 2023年5月27日
    00
  • 合作推广
    合作推广
    分享本页
    返回顶部