微信小程序,实现PDF文件上传

2,031 阅读3分钟

介绍

写的商户进件的小程序,原来只是上传图片。这周突然需要上传PDF,所以通过该文章记录相关内容...

小程序中上传文件

首先要明确的是小程序中并没有提供可直接选择手机本地资源文件的 API,原因主要在于iOS系统出于保护用户隐私的文件系统,APP 访问不到系统本地的文件,微信也不例外。

但是,微信小程序提供了选择会话中文件的功能,即wx.chooseMessageFile()接口。什么意思呢?就是比如我从电脑上发了一个文件给文件传输助手,那这个 API 就能在小程序中获取到这个文件的信息。获取到的文件信息包括:

  • path 本地临时文件路径(上传时要用到)
  • size 文件大小,单位为 B
  • name 文件名
  • type 文件类型

拿到了文件的信息后使用wx.uploadFile()接口发送请求上传选择好的 PDF 文件至后台,但是要注意的是编译运行前要勾选微信开发者工具中编辑器面板的详情中的不校验合法域名、web-view (业务域名)、TLS 版本以及 HTTPS 证书,原因请查阅小程序官方文档中的域名配置说明

// 小程序页面js文件
Page({
  data: {
    filePath: "", // 文件路径
    filename: "" // 文件名
  },
  chooseFile: function() { // 点击选择文件按钮触发事件
    var _that = this;
    wx.chooseMessageFile({ // 会话中选择文件API
      count: 1, // 可选文件个数
      type: 'file', // 文件类型
      success(res) { // 选择成功后的回调函数
        var size = res.tempFiles[0].size; // 文件大小
        var filename = res.tempFiles[0].name; // 文件名
        if (size > 4194301) { // 判断文件大小不能大于4M
          wx.showToast({ // 弹框提示
            title: '文件大小不能超过4MB!',
            icon: "none",
            duration: 2000,
            mask: true
          })
        } else if (filename.indexOf('.pdf') == -1) { // 判断文件格式必须为pdf
          wx.showToast({
            title: '文件格式必须为PDF!',
            icon: "none",
            duration: 2000,
            mask: true
          })
        } else {
          _that.setData({
            filePath: res.tempFiles[0].path, // 保存文件地址到data
            filename: filename // 保存文件名
          })
        }
      }
    })
  },
  uploadFile: function(){ // 上传文件
    var _that = this;
      wx.uploadFile({ // 本地资源上传到服务器API
        url: 'http://localhost:3000/uploadFile', // 指定服务器接口URL
        filePath: _that.data.filePath, // 本地文件路径,即选择文件返回的路径
        name: 'file', // 上传文件的key,后台要用到
        formData: { // 可额外添加字段,存于请求的body对象中
          'filename': _that.data.filename
        },
        success(res) {
          const data = res.data;
          ...
        }
      })
  }
})

下载存储的 PDF 文件至本地

既然用户上传了文件,就需要完成 PDF 文件的下载需求。

<!-- 下载文件组件download.vue -->
<template>
  <div>
    <!-- 使用了vuetify UI框架的组件, Markdown的代码高亮好像不识别了= = -->
    <v-btn @click="download">下载pdf到本地</v-btn>
  </div>
</template>

<script>
export default {
  name: "download",
  data() {
    return {
        filename: 'test.pdf' // 指定要下载的文件名,与后台uploads文件夹中存储的文件名一致
    };
  },
  methods: {
    download() {
      this.$axios({
        method: "post",
        url: "/api/downloadFile", // 请求URL
        data: {
          filename: this.filename // 请求参数
        },
        responseType: "blob" // 设置返回的数据类型为二进制数据
      })
        .then(response => {
          this.downloadFile(response); // 将返回结果作为参数调用本地下载文件方法
        })
        .catch(error => {});
    },
    downloadFile(data) {
        var _that = this;
      if (!data) {
        return;
      }
      let url = window.URL.createObjectURL(new Blob([data.data])); // 后台返回结果data是个对象,其中的data属性才是文件的二进制数据
      let link = document.createElement("a"); // 创建a标签
      link.style.display = "none"; // 设置a标签不可见
      link.href = url; // 设置a标签的URL属性
      link.setAttribute("download", _that.filename); // 给a标签添加download属性并指定下载的文件名(记得加后缀指定下载的文件格式)

      document.body.appendChild(link); // 将a标签节点添加在DOM中
      link.click(); // 触发a标签
    }
  }
};
</script>

总结

至此,完成了前端的文件上传和下载的描述。