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

下面是对于“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






拍照
上传
重置







  • 详解Vue中数组和对象更改后视图不刷新的问题

    下面是讲解”详解Vue中数组和对象更改后视图不刷新的问题”的攻略。 问题背景 在Vue中,当我们通过改变数组或对象的属性来更新数据时,Vue并不会立即将这个变化反映到视图上,而需要一些特殊的方法才能实现视图的更新。 解决方案 Vue提供了一些响应式的API来检测数据的变化,我们可以使用这些API来解决这个问题。 数组 当我们需要改变数组数据时,可以用以下几种…

    Vue 2023年5月29日
    00
  • vue实现文件上传读取及下载功能

    下面是“vue实现文件上传读取及下载功能”的完整攻略: 1. 文件上传功能实现 1.1. 简介 文件上传功能是指用户可以将文件选择或者拖拽到页面中的一个指定区域内,然后通过ajax上传给服务器。在vue中,可以使用 vue-upload-component 来实现文件上传。 1.2. 示例代码 安装vue-upload-component: npm inst…

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