vue 文件切片上传的项目实现

下面我将详细讲解“Vue 文件切片上传的项目实现”的完整攻略。该过程主要分为以下五个步骤:

  1. 安装依赖库

开发项目之前需要先安装以下库:

  • axios:用于发起后端请求;
  • element-ui:基于 Vue 的组件库,提供了上传文件的组件;
  • js-sha256:计算文件的哈希值。

可以使用以下命令进行安装:

npm install axios element-ui js-sha256 --save
  1. 前端实现

在代码中引用axiosElementUIjs-sha256库:

import axios from 'axios';
import { Upload, Message } from 'element-ui';
import sha256 from 'js-sha256';

接着,使用Upload组件制作上传文件的用户界面,核心代码如下:

<template>
  <div>
    <el-upload
      ref="upload"
      :action="serverPath"
      :auto-upload="false"
      :on-exceed="handleExceed"
      :file-list="fileList"
      :http-request="uploadChunk"
      :before-remove="beforeRemove"
      :on-progress="handleProgress"
      :on-success="handleSuccess"
      :on-error="handleError"
      multiple>
      <el-button type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">限制上传个数和单个文件大小</div>
    </el-upload>
  </div>
</template>

其中,ElUpload组件的各项属性含义如下:

  • action:文件上传后端接口地址;
  • auto-upload:是否在文件选取后立即上传;
  • on-exceed:文件数量超出限制时的回调函数;
  • file-list:用于展示已上传的文件列表;
  • http-request:发起上传请求的回调函数;
  • before-remove:文件删除前的确认提示;
  • on-progress:文件上传进度的回调函数;
  • on-success:文件上传成功的回调函数;
  • on-error:文件上传失败的回调函数;
  • multiple:是否允许多个文件上传。

接下来是文件上传的协议:

const uploadChunk = ({ file, onSuccess, onError }) => {
  const fileName = `${sha256(file.name)}-${file.size}`;
  const totalChunk = Math.ceil(file.size / CHUNK_SIZE);
  let uploadedChunks = 0;
  const loadNext = (chunkId) => {
    const start = chunkId * CHUNK_SIZE;
    const end = start + CHUNK_SIZE >= file.size ? file.size - 1 : start + CHUNK_SIZE - 1;
    const formData = new FormData();
    formData.append('chunkId', chunkId);
    formData.append('hash', fileName);
    formData.append('file', file.slice(start, end + 1));
    axios.post(`${serverPath}/${fileName}`, formData)
      .then((res) => {
        uploadedChunks += 1;
        if (uploadedChunks < totalChunk) {
          loadNext(chunkId + 1);
        } else {
          onSuccess({ fileName, fileUrl: res.data });
        }
      })
      .catch((err) => {
        onError(err);
      });
  };
  loadNext(0);
};

该协议将文件切成等大小的媒体块,并发送块的哈希值和块本身作为 POST 数据,因此要吸收每块上传的哈希值。当所有块上传成功时,后端服务器返回成功上传的文件地址并通过onSuccess函数在前端显示。否则,后端服务器没有必要将每块上传的哈希值保存到磁盘上,而是直接返回错误响应。

  1. 后端实现

后端需要编写一个接口用于处理上传文件的请求,并在该接口中实现请求中上传的媒体块的处理逻辑,这里以 Flask 为例:

from flask import Flask, request, jsonify
import os
from werkzeug.utils import secure_filename

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = '/tmp'

@app.route('/upload/<string:hash>', methods=['POST'])
def upload(hash):
    chunk_id = request.form.get('chunkId', type=int)
    filename = os.path.join(app.config['UPLOAD_FOLDER'], hash)
    if chunk_id == 0:
        # First request, remove if file exists
        if os.path.exists(filename):
            os.remove(filename)
    # Save the chunks of file
    with open(filename, 'ab') as f:
        f.write(request.files['file'].read())
    # Return upload file url after all chunk the file saved.
    if chunk_id == total_chunk - 1:
        return jsonify({'result': os.path.join(app.config['UPLOAD_FOLDER'], hash)})
    else:
        return '', 200

注意该实现的参数解释:

  • hash:上传文件的哈希值;
  • chunkId:媒体块的 ID;
  • filename:上传文件名;
  • totalChunk:媒体块的总数。

这里将每块上传的媒体块存储到文件中,在每次上传媒体块后都会将其添加到同一文件中。传输结束时,返回上传文件的 URL。

  1. 合并上传的文件

当文件块全部上传完成后,后端服务器将返回上传文件的 URL。这里我们需要编写一个自动合并上传文件的函数:

const mergeChunks = async (fileName, fileUrl) => {
  const { data: serverFileUrl } = await axios.post(`${serverPath}/merge`, {
    hash: fileName,
    fileUrl,
  });
  return serverFileUrl;
};

在这段代码中,我们向后端服务器发送一个包含上传文件的哈希值和每个块的 URL 的请求。返回的结果是上传文件的所有块已经成功上传到服务器并被合并。合并完成后,我们将接收到的上传文件的 URL 作为最终结果返回。

  1. 完整代码

完整代码如下:

<template>
  <div>
    <el-upload
      ref="upload"
      :action="serverPath"
      :auto-upload="false"
      :on-exceed="handleExceed"
      :file-list="fileList"
      :http-request="uploadChunk"
      :before-remove="beforeRemove"
      :on-progress="handleProgress"
      :on-success="handleSuccess"
      :on-error="handleError"
      multiple>
      <el-button type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">限制上传个数和单个文件大小</div>
    </el-upload>
  </div>
</template>

<script>
import axios from 'axios';
import { Upload, Message } from 'element-ui';
import sha256 from 'js-sha256';

const serverPath = '/upload';
const CHUNK_SIZE = 1024 * 1024;
export default {
  name: 'FileUpload',
  data() {
    return {
      fileList: [],
    };
  },
  methods: {
    handleExceed(files, fileList) {
      Message.warning(`只能选择一个或者一个以上文件`);
    },
    beforeRemove(file, fileList) {
      const fileName = `${sha256(file.name)}-${file.size}`;
      axios.delete(`${serverPath}/${fileName}`)
        .then((res) => {
          if (res.status === 204) {
            Message.success(`删除文件 ${file.name} 成功`);
          }
        })
        .catch((err) => {
          Message.error(`删除文件 ${file.name} 失败`);
        });
    },
    handleProgress({ file, fileList }) {
      const uploadPercent = Math.ceil(file.uploadedChunks / file.totalChunk * 100);
      file.percent = uploadPercent;
      this.fileList = [...fileList];
    },
    handleSuccess({ fileName, fileUrl }, file, fileList) {
      this.mergeChunks({ fileName, fileUrl })
        .then((url) => {
          file.url = url;
          this.fileList = [...fileList];
          Message.success(`${file.name} 上传完成`);
        })
        .catch((err) => {
          Message.error(`${file.name} 上传失败`);
        });
    },
    handleError({ action, response, file, fileList }) {
      Message.error(`${file.name} 上传失败`);
    },
    mergeChunks: async (fileName, fileUrl) => {
      const { data: serverFileUrl } = await axios.post(`${serverPath}/merge`, {
        hash: fileName,
        fileUrl,
      });
      return serverFileUrl;
    },
    uploadChunk: ({ file, onSuccess, onError }) => {
      const fileName = `${sha256(file.name)}-${file.size}`;
      const totalChunk = Math.ceil(file.size / CHUNK_SIZE);
      let uploadedChunks = 0;
      const loadNext = (chunkId) => {
        const start = chunkId * CHUNK_SIZE;
        const end = start + CHUNK_SIZE >= file.size ? file.size - 1 : start + CHUNK_SIZE - 1;
        const formData = new FormData();
        formData.append('chunkId', chunkId);
        formData.append('hash', fileName);
        formData.append('file', file.slice(start, end + 1));
        axios.post(`${serverPath}/${fileName}`, formData)
          .then((res) => {
            uploadedChunks += 1;
            if (uploadedChunks < totalChunk) {
              loadNext(chunkId + 1);
            } else {
              onSuccess({ fileName, fileUrl: res.data });
            }
          })
          .catch((err) => {
            onError(err);
          });
      };
      loadNext(0);
    },
  },
};
</script>

再看一段后端 Flask 代码:

from flask import Flask, request, jsonify
import os
from werkzeug.utils import secure_filename

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = '/tmp'

@app.route('/upload/<string:hash>', methods=['POST'])
def upload(hash):
    chunk_id = request.form.get('chunkId', type=int)
    filename = os.path.join(app.config['UPLOAD_FOLDER'], hash)
    if chunk_id == 0:
        # First request, remove if file exists
        if os.path.exists(filename):
            os.remove(filename)
    # Save the chunks of file
    with open(filename, 'ab') as f:
        f.write(request.files['file'].read())
    # Return upload file url after all chunk the file saved.
    if chunk_id == total_chunk - 1:
        return jsonify({'result': os.path.join(app.config['UPLOAD_FOLDER'], hash)})
    else:
        return '', 200

@app.route('/upload/merge', methods=['POST'])
def merge():
    hash = request.form.get('hash', type=str)
    file_url = request.form.get('fileUrl', type=str)

    filename = os.path.join(app.config['UPLOAD_FOLDER'], hash)
    with open(filename, 'ab') as f:
        for chunk_url in file_url:
            with open(chunk_url, 'rb') as chunk:
                f.write(chunk.read())

    # delete chunk file
    for chunk_url in file_url:
        os.remove(chunk_url)

    return jsonify({'result': os.path.join(app.config['UPLOAD_FOLDER'], hash)})

注意在该代码中的参数解释:

  • hash:上传文件的哈希值;
  • chunkId:媒体块的 ID;
  • filename:上传文件名;
  • totalChunk:媒体块的总数。

以上就是实现 Vue 文件切片上传的完整攻略。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:vue 文件切片上传的项目实现 - Python技术站

(0)
上一篇 2023年5月28日
下一篇 2023年5月28日

相关文章

  • vue中动态组件使用及传值方式

    接下来我会详细讲解“Vue中动态组件使用及传值方式”的完整攻略,包含以下内容: 动态组件的基本使用方法 在父组件中使用动态组件 在子组件中使用 props 传递数据 示例代码说明 1. 动态组件的基本使用方法 Vue中的动态组件可以让我们根据不同的情况加载不同的组件。在 Vue 中使用动态组件非常简单,只需要使用 component 标签,并将 is 属性指…

    Vue 2023年5月27日
    00
  • Springboot vue导出功能实现代码

    那我为您详细讲解一下“Springboot vue导出功能实现代码”的完整攻略。 1. 环境准备 首先需要准备好以下环境: JDK 8+ Maven 3+ Vue.js 2+ Element-UI 2+ axios 0.19+ 2. 前端实现 在前端页面中,我们需要添加一个导出按钮,当用户点击该按钮时,触发导出操作。 <el-button type=&…

    Vue 2023年5月27日
    00
  • Vue接口封装的完整步骤记录

    以下是Vue接口封装的完整步骤记录的攻略: 1. 确定接口需求 在进行接口封装之前,首先需要明确接口的需求,包括但不限于接口的用途、参数、API文档等。这可以帮助我们更好地理解业务需求,从而设计出更好的API接口。 具体来说,需要确定以下几点: 接口的用途:明确该接口的作用,它是提供了什么功能?这对我们后续的接口设计有很大的帮助。 接口的参数:需要确定接口所…

    Vue 2023年5月27日
    00
  • Vue CLI中模式与环境变量的深入详解

    下面是Vue CLI中模式与环境变量的深入详解。 什么是Vue CLI Vue CLI是Vue.js官方提供的脚手架工具,用于快速搭建Vue.js应用。Vue CLI提供了许多功能,包括创建项目、执行开发服务器和构建打包等。在Vue CLI中,有三种不同的模式(modes)可供选择,分别是开发模式(development)、生产模式(production)和…

    Vue 2023年5月28日
    00
  • vue使用反向代理解决跨域问题方案

    使用反向代理可以解决vue应用中遇到的跨域问题。下面是具体的攻略: 1. 安装http-proxy-middleware 在vue cli 3的脚手架中,已经默认安装了http-proxy-middleware依赖,无需再次安装。如果是手动创建的vue项目,则需要使用npm或yarn进行安装: npm install http-proxy-middlewar…

    Vue 2023年5月28日
    00
  • Vue3基于countUp.js实现数字滚动的插件

    Vue3基于countUp.js实现数字滚动的插件就是一个可以在Vue3中方便地实现数字滚动效果的插件。下面将介绍实现该插件的完整攻略: 确认需求和安装countUp.js 首先需要明确需求,确认需要实现数字滚动的具体元素和动画效果等。然后需要安装countUp.js插件,在Vue3项目中引入countUp.js的CDN链接或安装countUp.js的NPM…

    Vue 2023年5月27日
    00
  • vue中的事件修饰符once,prevent,stop,capture,self,passive

    下面我将详细讲解Vue中的事件修饰符。 什么是事件修饰符 Vue允许在利用v-on绑定事件时添加事件修饰符,以便对事件的一些特殊处理。Vue为我们提供了6种常用的事件修饰符,分别是v-once、v-prevent、v-stop、v-capture、v-self和v-passive。 事件修饰符示例 v-once 当我们需要对某个事件仅绑定一次,可以使用v-o…

    Vue 2023年5月27日
    00
  • 在vue中使用vant TreeSelect分类选择组件操作

    下面是关于在vue中使用vant TreeSelect分类选择组件的详细攻略。 1. 安装vant 首先,我们需要安装vant组件库,可以使用npm或yarn来安装,命令如下: npm install vant -S // 使用npm yarn add vant // 使用yarn 2. 引入需要使用的TreeSelect组件 在需要使用组件的.vue文件中…

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