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.js配合$.post从后台获取数据简单demo分享

    下面介绍关于vue.js配合$.post从后台获取数据简单demo的详细攻略。 步骤一:准备工作 在实现这个demo前,需要准备一些工作: 安装vue.js库 引入jQuery库 编写后台接口 步骤二:编写代码 先在HTML中引入vue.js和jQuery库,并且新建一个Vue实例对象: <!DOCTYPE html> <html> …

    Vue 2023年5月27日
    00
  • element日期选择器el-date-picker样式图文详解

    标题 element日期选择器样式图文详解 简介 element日期选择器(el-date-picker)是一款常用的日期选择器组件,可用于各类web应用中。本文将详细讲解该组件的各种样式,以便对该组件的使用和美化提供帮助。 el-date-picker基础样式 el-date-picker组件可以设置的样式非常丰富,整体样式可以通过el-date-pick…

    Vue 2023年5月29日
    00
  • vue使用pdf.js预览pdf文件的方法

    下面是“Vue使用PDF.js预览PDF文件的方法”的完整攻略。 步骤一:安装PDF.js库 首先,我们需要在我们的Vue项目中安装pdfjs-dist依赖: npm install pdfjs-dist –save 步骤二:加载PDF.js文件 在我们的Vue组件中,加载PDF.js文件: <template> <div> &lt…

    Vue 2023年5月28日
    00
  • vue中filters 传入两个参数 / 使用两个filters的实现方法

    在 Vue 中,可以使用 filter 过滤器来处理模板中的数据。filter 在显示数据之前对其进行处理,比如对字符串格式化、将时间格式化等等。 在 Vue 中,我们可以定义一个 filter 来处理一些数据,此时这个 filter 就是一个全局的 filter。可以被任何组件进行调用。 现在我们来详细讲解“vue中filters 传入两个参数 / 使用两…

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