应用开发平台前端集成vue-simple-uploader实现文件分块上传

这篇具有很好参考价值的文章主要介绍了应用开发平台前端集成vue-simple-uploader实现文件分块上传。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

文件的上传是系统的必备功能,Element提供了上传组件upload,也基本能满足常见常用的文件上传功能,特别是应对小型文件(10M以下)的处理。但如果是遇到要求更多更高的场景,上传几百兆甚至上G的视频文件,要求分块上传,能断点续传,显示进度,能暂停,能重试……这时候就显得乏力了。如果基于upload实现,需要附加大量的二次开发,这未必是一种最佳实现方案。
这时候,就需要找一找看一看,市面上是否有现成的“轮子”可用了。

上一篇介绍了开发平台中关于文件处理的需求、方案和整体设计,今天首先来说下前端那些事儿。

技术选型

vue-simple-uploader,作者对vue3做了适配。
官网 https://github.com/simple-uploader/vue-uploader/blob/vue3/README_zh-CN.md

特性

  • 支持文件、多文件、文件夹上传
  • 支持拖拽文件、文件夹上传
  • 统一对待文件和文件夹,方便操作管理
  • 可暂停、继续上传
  • 错误处理
  • 支持“快传”,通过文件判断服务端是否已存在从而实现“快传”
  • 上传队列管理,支持最大并发上传
  • 分块上传
  • 支持进度、预估剩余时间、出错自动重试、重传等操作

功能设计

组件提供了通用能力,整合到平台中,需要根据需求做定制和集成。
在本平台中,我使用该上传组件,主要解决的是与业务实体关联的附件,不同场景下文件有大有小。
需要支持文件、多文件上传,但不需要直接上传文件夹(上传文件夹通常做文档库、网盘场景中需要)。
暂停、重试、分块、预估时间、进度显示、重传这些是需要的,但快传、秒传功能不需要(快传、秒传往往只在互联网应用的网盘应用场景有需求,企业应用里都是些独立的,不重复的文件)。

安装及注册

安装,执行如下命令

pnpm install vue-simple-uploader@next --save

初始化,修改main.js,全局注册

import uploader from 'vue-simple-uploader'
import 'vue-simple-uploader/dist/style.css'

// 创建实例
const setupAll = async () => {
  const app = createApp(App)

  await setupI18n(app)

  setupStore(app)

  setupGlobCom(app)

  setupRouter(app)

  setupPermission(app)

  ……略

  // 文件上传
  app.use(uploader)

  app.mount('#app')
}

封装组件

上传组件

<template>
  <uploader
    ref="uploader"
    :options="finalOptions"
    :file-status-text="statusText"
    :show-success-files="showSuccessFiles"
    :single-flag="singleFlag"
    :auto-start="autoStart"
    :show-list-flag="showListFlag"
    @file-complete="fileComplete"
    @file-added="fileAdded"
  />
</template>

<script>
import { getToken } from '@/utils/auth'
export default {
  components: {},
  props: {
    options: {
      type: Object,
      required: false,
      default() {
        return {}
      }
    },
    singleFlag: {
      type: Boolean,
      default: false
    },
    autoStart: {
      type: Boolean,
      default: true
    },
    // 是否显示文件列表
    showListFlag: {
      type: Boolean,
      default: true
    },
    entityType: {
      type: String,
      required: true
    },
    entityId: {
      type: String,
      default: '',
      required: true
    },
    moduleCode: {
      type: String,
      required: true
    },
    showSuccessFiles: {
      type: Boolean,
      default: false,
      required: false
    },
    serverUrl: {
      type: String,
      default: '',
      required: true
    }
  },
  data() {
    const token = getToken()
    return {
      defaultOptions: {
        target: import.meta.env.VITE_BASE_URL + this.serverUrl,
        testChunks: false,
        maxChunkRetries: 3,
        chunkSize: 10485760,
        query: {
          entityType: this.entityType,
          entityId: this.entityId,
          moduleCode: this.moduleCode
        },
        headers: { 'X-Token': token },
        generateUniqueIdentifier: () => {
          // 业务主键+时间戳最大限度降低并发冲突发生的概率
          return this.entityId + new Date().getTime()
        },
        parseTimeRemaining(timeRemaining, parsedTimeRemaining) {
          return parsedTimeRemaining
            .replace(/\syears?/, '年')
            .replace(/\days?/, '天')
            .replace(/\shours?/, '时')
            .replace(/\sminutes?/, '分')
            .replace(/\sseconds?/, '秒')
        }
      },
      statusText: {
        success: '100%',
        error: '失败',
        uploading: '上传中',
        paused: '暂停中',
        waiting: '等待中'
      }
    }
  },
  computed: {
    finalOptions: function () {
      const opts = Object.assign(this.defaultOptions, this.options)
      return opts
    }
  },
  watch: {
    entityId: function () {
      this.$refs.uploader.options.query.entityId = this.entityId
    }
  },
  methods: {
    fileComplete(file) {
      this.$emit('fileComplete', file)
    },
    fileAdded(file) {
      this.$emit('fileAdded', file)
    }
  }
}
</script>

有几个需要注意的点,下面来说说。
首先,为了简化使用方设置,我先通过defaultOptions,将组件大部分公用配置设置好,作为缺省配置。同时将可能需要设置的属性作为props提供出来,供使用方按需调整。
其次,受组件自身限制,没法走前端的api调用框架axios,而是在组件配置options的target属性指定了文件上传的后端地址。
再次,平台使用JWT做身份认证,绕过api调用框架axios的结果就是没有自动附加token,同样需要在组件配置中附加,否则后端会视为未授权。

 const token = getToken()
    return {
      defaultOptions: {
       ……
      headers: { 'X-Token': token }
       ……

最后,我加入了平台自己的控制,即将模块编码moduleCode,实体类型entityType和实体标识entityId也传入了进来,并通过组件的query属性传给了后端,这几个参数,用于后端来生成结构化的保存路径。
image.png
这么做的主要目的,除了分门别类存储附件外,还有个显著优势可以按需进行附件归档。比如系统运行3年了,磁盘占用越来越大,业务上通常只需要查看最近一年的单据和附件,那么就可以把1年前的附件,从服务器上移动到备份服务器上。

默认UI丑到爆,需要自己调整
image.png
调整后效果如下:
image.png

管理组件

管理组件通常是配合上传组件一起使用的,对上传结果进行预览、验证和移除,如下图所示。

image.png
vue-simple-uploader组件虽然有文件列表,但这个列表实际是有功能缺失的,无法下载,更无法移除,因此自己另行封装了一个。

<template>
  <el-table :data="entityData" highlight-current-row border>
    <el-table-column type="index" label="序号" sortable width="65" />
    <el-table-column prop="name" label="名称" show-overflow-tooltip />
    <el-table-column prop="size" label="大小" width="80" />

    <el-table-column prop="createTime" label="时间" width="100" />
    <el-table-column fixed="right" label="操作" width="200">
      <template #default="scope">
        <el-button
          type="primary"
          icon="Download"
          class="header-search_button"
          size="small"
          @click="download(scope.row)"
          >下载</el-button
        >
        <el-button
          type="primary"
          icon="Delete"
          class="header-search_button"
          size="small"
          @click="remove(scope.row)"
          >删除</el-button
        >
      </template>
    </el-table-column>
  </el-table>
</template>

<script>
export default {
  components: {},
  props: {
    entityId: {
      type: String,
      default: '',
      required: true
    },
    showDelete: {
      type: Boolean,
      default: true,
      required: false
    }
  },
  data() {
    return {
      entityData: []
    }
  },
  watch: {
    entityId: function () {
      this.list()
    }
  },
  mounted: function () {
    this.list()
  },
  methods: {
    list() {
      if (this.entityId) {
        // 只有当entityId不为空时才发起查询
        this.$api.support.attachment.list({ entityId: this.entityId }).then((res) => {
          this.entityData = res.data
        })
      }
    },
    remove(row) {
      this.$confirm('是否删除该附件?', '确认', {
        type: 'warning'
      })
        .then(() => {
          this.$api.support.attachment.remove(row.id).finally(() => {
            this.list()
          })
        })
        .catch(() => {
          this.$message.info('已取消')
        })
    },
    download(row) {
      this.$api.support.attachment.download(row.id, row.name)
    }
  }
}
</script>

浏览组件

浏览组件通常用于查看视图,即查看当前单据上传了哪些附件,并可以下载,但不能删除,如下图所示:
image.png

<template>
  <el-row>
    <ul style="margin: 0px; padding: 0px">
      <li
        v-for="item in entityData"
        :key="item.id"
        style="
          line-height: 30px;
          width: 100%;
          overflow: hidden;
          word-break: keep-all;
          white-space: nowrap;
          text-overflow: ellipsis;
        "
      >
        <a :title="item.name" @click="download(item)" class="cursor-pointer">
          <span>&nbsp;&nbsp;{{ item.name }}</span>
          <span>({{ item.size }} )</span>
        </a>
      </li>
    </ul>
  </el-row>
</template>
<script>
export default {
  props: {
    entityId: {
      type: String,
      default: '',
      required: true
    }
  },
  data() {
    return {
      entityData: []
    }
  },
  watch: {
    entityId: function () {
      this.list()
    }
  },
  mounted: function () {
    this.list()
  },
  methods: {
    list() {
      if (this.entityId) {
        // 只有当entityId不为空时才发起查询
        this.$api.support.attachment.list({ entityId: this.entityId }).then((res) => {
          this.entityData = res.data
        })
      }
    },
    download(item) {
      this.$api.support.attachment.download(item.id)
    }
  }
}
</script>

遗留问题(已解决)

同时使用封装的上传组件和管理组件,会出现下面这种情况,也会影响用户体验。
image.png
官方控件自带的上传文件列表,上传成功的文件没有属性或方法从列表中移除。我尝试过使用fileSuccess事件自己实现逻辑,如下图所示:

 fileSuccess(rootFile, file) {
      //  文件上传成功后从列表中移除
      // TODO:以下代码未调试成功
      // let index = this.$refs.uploader.fileList.findIndex(
      //   (successFile) => successFile.id === file.id
      // )
      // if (index !== -1) {
      //   console.log(this.$refs.uploader.fileList)
      //   this.$refs.uploader.fileList.splice(index, 1)
      //   console.log(this.$refs.uploader.fileList)
      // }
    }
  }

实际调试发现fileList并不能按预期正常变更集合元素,原因不明。后续有时间了再处理,要么将上面代码逻辑调整好,要么直接基于源码去修改。

2023-11-26 填坑
查阅vue-simple-uploader使用的组件simple-uploader.js文档,并经过摸索尝试,使用removeFile来移除已上传的文件,测试通过

    fileSuccess(rootFile) {
      this.$refs.uploader.uploader.removeFile(rootFile)
    }

注意需要调用到内部uploader组件。
同时推测之前操作fileList不成功,是因为fileList组件从根本上是读取uploader里的文件数据。

开发平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:csdn专栏
开源地址:Gitee
开源协议:MIT
欢迎收藏、点赞、评论,你的支持是我前行的动力。文章来源地址https://www.toymoban.com/news/detail-473688.html

到了这里,关于应用开发平台前端集成vue-simple-uploader实现文件分块上传的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Util应用框架快速入门(4) - 集成测试开发入门

    本文演示Util应用框架开发的项目中如何编写集成测试. 完成 Web Api 快速入门,本文将在之前生成的示例项目上讲解集成测试的开发. 自动化测试对于Util应用框架的开发非常重要,它能保证基础功能的稳定性. 对于使用 Util 开发的业务项目,自动化测试不是必须的,但掌握它可能很有

    2024年02月08日
    浏览(54)
  • Python应用-web应用开发(上)前端part

    版权声明:转载请联系我获得授权 参考视频:Python的web开发全家桶 参考学习曲线:Python入门技能树 在网页右键点检查可查看源码 选中点击想要的区域可以得到对应源码区域 styles中为样式定义 咱们网站与别人的不一样: Flask框架为了让咱们写标签方便,支持将字符串写入到

    2024年02月04日
    浏览(40)
  • Android 平台应用软件开发(学习中)

    1,LinearLayout(线性布局),RelativeLayout(相对布局),FrameLayout(帧布局),AbsoluteLayout(绝对布局),TableLayout(表格布局)。 2,线性布局中的控件属性说明 ①android:background,设置UI控件的背景,其值可以是资源文件夹中的图片或者是颜色的十六进制值。 ②android:orientation,该属性是线性布局

    2024年02月04日
    浏览(42)
  • Flutter:跨平台移动应用开发的未来

    Flutter的背景和概述 Flutter是由Google开发的一个开源UI工具包,用于构建漂亮、快速且高度可定制的移动应用程序。它于2017年首次发布,并迅速引起了开发者们的关注。Flutter采用了一种全新的方法来构建用户界面,通过使用自绘UI技术,可以实现高性能的跨平台应用开发。 Fl

    2024年01月22日
    浏览(92)
  • 基于Unity平台开发Vision Pro应用

    VisionOS是苹果最新空间计算设备Vision Pro的操作系统。Unity开发人员可以利用现有的3D场景 以及为 visionOS 构建游戏或应用程序的资产。有关 visionOS 的更多信息,请参阅 Apple 的 visionOS 概述。 visionOS提供了几种不同的显示应用程序的模式:Windows、Volumes或Spaces。用户可以使用Wind

    2024年01月22日
    浏览(51)
  • 通过PyCharm平台开发Django应用程序

    学会使用命令行工具开发Django应用程序是基础,不过更多的时候还是要借助平台开发工具。目前,最好的Django应用程序开发工具就是jetBrains公司推出的PyCharm平台了。 借助PyCharm开发平台,可以极大提高开发Django应用程序的效率,同时可以使用到很多非常实用的第三方插件。不

    2024年04月26日
    浏览(39)
  • 前端桌面应用开发实践:Electron入门指南

    随着互联网的快速发展,前端开发不再局限于网页应用,而是逐渐涉及到桌面应用的开发。Electron作为一种流行的前端桌面应用开发框架,为开发者提供了一种快速构建跨平台桌面应用的方式。本文将介绍Electron的基本概念和使用方法,并通过一个简单的示例来说明其开发实践

    2024年02月11日
    浏览(46)
  • 盘点 | 跨平台桌面应用开发的5大主流框架

    受益于开源技术的发展,以及响应快速开发的实际业务需求,跨平台开发不仅限于移动端跨平台,桌面端虽然在市场应用方面场景不像移动端那么丰富,但也有市场的需求。 相对于个人开发者而言,跨平台框架的使用,主要为了满足以下三个主要能力: 生产力提升:框架能

    2024年02月07日
    浏览(68)
  • 微信小程序 | 微信公众平台SpringBoot开发实例 │ 模板消息的应用开发

     在手机微信公众号中输入文本(如“你好”),公众号发送两条模板消息,如下图所示。 模板消息用来帮助公众号进行业务通知,是在模板内容中设定参数(参数必须以{ {开头,且以.DATA} }结尾)并在调用时为这些参数赋值并发送的消息。模板消息仅用于向用户发送重要的服务

    2024年02月03日
    浏览(51)
  • 【Android应用开发之前端——简易App登录页面】

    各家App的登录页面大同小异,要么是用户名和密码组合登录,要么是手机号和验证码组合登录。如果要做的更好一点,就要提供忘记密码与记住密码等功能。我们的App登录项目把这些功能综合一下,都呈现到页面上。先将效果图奉上: 使用密码登录: 使用验证码登录: 修改

    2024年02月09日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包