likes
comments
collection
share

微信小程序开发实战-文件上传下载专题

作者站长头像
站长
· 阅读数 83

由于采用的是Taro3.x+Vue3+NutUI3.X+TSX多端构建开发小程序的,所以先简述下这个解决方案

1、微信小程序-开放式跨端跨框架解决方案

Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发 微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ / 飞书 小程序 / H5 / RN 等应用。

现如今市面上端的形态多种多样,Web、React Native、微信小程序等各种端大行其道。当业务要求同时在不同的端都要求有所表现的时候,针对不同的端去编写多套代码的成本显然非常高,这时候只编写一套代码就能够适配到多端的能力就显得极为需要。

Taro写小程序其实大部分API还是调取微信的,只是WX.变成了Taro.,还对其进行了一定的补充。

1、文件上传(原生开发的话只需要把Taro.改成wx.)

文件上传

      Taro.chooseMessageFile({
        count: props.maxNum,
        type: 'file',
        success: function(res: any) {
          console.log(res.tempFiles, res.tempFiles[0].path)
          const suffix = res.tempFiles[0].path.split('.')[1]
          if (!props.fileType.includes(suffix)) {
            Toast('请选择xls或xlsx文件上传')
            return
          }
          state.fileName = res.tempFiles[0].name
          if (props.uploadWay) {//选中文件,暂不上传
            state.fileList.push(res.tempFiles[0])
            emitData()
          } else {//选中文件就上传
            Taro.showLoading({
              title: '上传中'
            })
            Taro.uploadFile({
              url: baseUrl + `/api/v1/file/${props.uploadType}/upload`, // 仅为示例,非真实的接口地址
              filePath: res.tempFiles[0].path,
              name: 'file',
              header: { Authorization: 'Bearer ' + useUserStore().token },
              formData: {
                'enterpriseId': props.enterpriseId
              },
              success(res: any) {
                const { success, content } = JSON.parse(res.data)
                if (success) state.fileList.push(content)
                emitData()
              },
              complete: function() {
                Taro.hideLoading()
              }
            })
          }
        }
      })

2、文件下载

      downTemplate().then((res) => {
        const { success, content } = res
        success && Taro.downloadFile({
          url: content, // 仅为示例,并非真实的资源
          success: function(res) {
            // 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容
            if (res.statusCode === 200) {
              const filePath = res.tempFilePath
              Taro.openDocument({
                filePath: filePath,
                showMenu: true,
                success: function() {
                  console.log('打开文档成功')
                }
              })
            }
          },
          complete: function() {
            state.downdisabled = false
          }
        })
      }).finally(() => state.downdisabled = false)

3、文件流写入

responseType: 'arraybuffer',这个别忘了配置

// 台账模块-下载模板
export function downTemplate(params) {
  return request(
    {
      url: baseUrl + `/api/v1/tracedata/template/${params.fieldName}`,
      method: 'get',
      responseType: 'arraybuffer',
      params
    }
  )
}
const uploadTemplete = () => {
        state.downdisabled = true
        downTemplate(state.searchParams).then((res) => {
          const fs = Taro.getFileSystemManager() // 全局唯一的文件管理器
          const fileName = useUserStore()?.userInfo?.enterpriseName + '_' + useDidShowStore()?.placeInfo?.fieldNameDisplay + '_' + dayjs().format('YYYYMMDD') + '.xlsx'
          fs.writeFile({
            filePath: Taro.env.USER_DATA_PATH + '/' + fileName, // 这里填文件的名字
            data: res,
            encoding: 'utf-8',
            success() {
              Taro.openDocument({
                showMenu: true,
                filePath: Taro.env.USER_DATA_PATH + '/' + fileName,
                success: function() {
                  state.downdisabled = false
                }
              })
            }
          })
        }).finally(() => state.downdisabled = false)
      }

这里如果想兼容PC端小程序和移动端小程序,需要加上判断,调用不同端的API。

Taro.getSystemInfo({
  success: (res) => {
    // windows | mac为pc端
    // android | ios为手机端
    if (['windows', 'mac'].includes(res.platform)) {
      Taro.saveFileToDisk({
        filePath: filePath,
        success(res) {
          console.log(res)
        },
        fail(res) {
          console.error(res)
        }
      })
    } else {
      Taro.openDocument({
        showMenu: true,
        filePath: filePath,
        success: function() {
          state.downdisabled = false
        }
      })
    }
  }
})

注意:webView方式的H5页面下载文件功能,有时候会被微信浏览器拦截,有时不会。解决方案也很简单,放在小程序内下载,实现方式可以是直接在小程序内也可以是交叉进行。

2、webview内嵌H5解决PC端文件无法上传问题

通过H5的形式,就没有局限性了,可以随意发挥,使用UI框架的上传也好,自己写原生的也简单,我这里用的是京东的NutUI。引入微信的jssdk,实现H5与原生小程序之间的交互。

1、文件上传

import {useAttrs, defineComponent, reactive, ref} from 'vue'
import {Uploader, Dialog, Toast} from '@nutui/nutui'
import {getizhuisuUrl, getHeaders} from '@/api'
import {useExpose} from '@/components/it/utils'
import {goBack} from '@/utils'
const icon_upload = require('@/assets/images/izhuisu/icon_upload.png')
export default defineComponent({
  name: 'FileUpload',
  props: {
    prop: {
      type: String,
      default: ''
    },
    fetchParams: {
      type: Object,
      default: () => ({})
    },
    uploadUrl: {
      type: String,
      default: ''
    },
    fileList: {
      type: Array,
      default: () => []
    },
    max: {
      type: Number,
      default: 1
    },
    maxSize: {
      type: Number || String,
      default: 20 * 1024 * 1024 // 默认最大5M
    },
    formData: {
      type: Object,
      default: () => { }
    },
    timeoutTime: {
      type: Number,
      default: 1000 * 120
    }
  },
  setup(props) {
    const uploadRef = ref()
    const attrs: any = useAttrs()
    const state = reactive({
      fileList: props.fileList
    })
    const deleteFile = () => {
      uploadRef.value.onDelete()
    }
    const onSuccess = () => goBack()
    const onFailure = ({responseText}) => {
      // Toast.fail(JSON.parse(responseText).errors[0].message)
      Dialog({
        title: '提示',
        content: JSON.parse(responseText).errors[0].message,
        noCancelBtn: true
      })
      deleteFile()
    }
    const beforeUpload = (file) => {
      const fileType = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel']
      if (!fileType.includes(file[0].type)) {
        Toast.warn('文件格式不正确,仅支持xlsx和xls格式')
        return new Promise((resolve) => resolve([]))
      } else {
        return new Promise((resolve) => resolve([file[0]]))
      }
    }
    const onChange = () => {
      console.log(11111)
    }
    // 获取 tableRef 实例
    const getSumbit = () => uploadRef.value.submit()
    useExpose({
      getSumbit
    })
    return () => (
      <>
        <Uploader
          autoUpload={false}
          ref={uploadRef}
          url={getizhuisuUrl(props.uploadUrl)}
          accept='.xls,.xlsx'
          headers={getHeaders()}
          data={props.formData}
          maximum={props.max}
          vModel:fileList={state.fileList}
          with-Credentials='true'
          onSuccess={onSuccess}
          onFailure={onFailure}
          maximize={props.maxSize}
          timeout={props.timeoutTime}
          {...props}
          {...attrs}
          upload-icon={icon_upload}
          beforeUpload={beforeUpload}
          onChange={onChange}
        >
        </Uploader>
      </>
    )
  }
})

2、文件下载

responseType: 'blob',这个别忘了配置

3、滑动事件适配

思考

所有的移动端的组件库(比如有赞的VantUI、京东的nutUI等),因此默认只适配了移动端设备,这意味着组件只监听了移动端的 touch 事件,没有监听桌面端的mouse 事件。

解决方案

思路很简单,只要让将 mouse 事件转换成对应的 touch 事件,问题不就迎刃而解了。

有赞团队就很贴心,直接安排明白

如果你需要在桌面端使用 移动端组件库(不局限于Vant,都可以用),可以引入有赞团队提供的 @vant/touch-emulator,这个库会在桌面端自动将 mouse 事件转换成对应的 touch 事件,使得组件能够在桌面端使用。

  1. 安装模块

npm i @vant/touch-emulator -S
  1. 引入模块后自动生效
import '@vant/touch-emulator';

写在最后

我是凉城a,一个前端,热爱技术也热爱生活。

与你相逢,我很开心。