资讯专栏INFORMATION COLUMN

大文件上传

xushaojieaaa / 2242人阅读

摘要:大文件上传的实现准备阶段大文件上传的过程中需要用到发布订阅模式监听上传的进度发布订阅模式的实现事件栈获取事件对应栈的索引事件类型对应栈的索引不存在为已有事件类型处理栈监听事件自定义事件类型事件处理函数已存在事件类型处理直接把相应的处理函

大文件上传的实现

准备阶段:
大文件上传的过程中需要用到发布订阅模式监听上传的进度
发布订阅模式的实现

export default class {
    // 事件栈
    eventStacks = [{
        eventType: "",
        handlers: []
    }];
    /**
     * 获取事件对应栈的索引
     *
     * @param {string} eventType 事件类型
     * @return {number} stackIndex 对应栈的索引 不存在为-1
     */
    indexOf(eventType) {
        const eventStacks = this.eventStacks;
        // 已有事件类型处理栈
        let stackIndex = -1;
        for (let i = 0; i < eventStacks.length; i++) {
            const eventStack = eventStacks[i];
            if (eventStack.eventType === eventType) {
                stackIndex = i;
                break;
            }
        }
        return stackIndex;
    };
    /**
     * 监听事件
     *
     * @param {string} eventType 自定义事件类型
     * @param {Function} handler 事件处理函数
     */
    on(eventType, handler) {
        const index = this.indexOf(eventType);
        if (index >= 0) {
            // 已存在事件类型处理 直接把相应的处理函数入栈
            this.eventStacks[index].handlers.push(handler);
        }
        else {
            // 不存在事件,把对应的事件处理入栈
            const newEventStack = {
                eventType,
                handlers: [handler]
            };
            this.eventStacks.push(newEventStack);
        }
    };
    /**
     * 触发对应的事件
     *
     * @param {string} eventType 自定义事件类型
     * @param {Object} params 参数对象
     */
    emit(eventType, params = {}) {
        this.execEvent(eventType, params);
    };
    /**
     * 执行对应的事件
     *
     * @param {string} eventType 自定义事件类型
     * @param {Object} params 参数对象
     */
    execEvent(eventType, params = {}) {
        const index = this.indexOf(eventType);
        if (index < 0) {
            return;
        }
        const handlers = this.eventStacks[index].handlers;
        for (let i = 0; i < handlers.length; i++) {
            const currentHandler = handlers[i];
            if (currentHandler && typeof currentHandler === "function") {
                currentHandler(params);
            }
        }
    };
    /**
     * 解除对应的事件
     *
     * @param {string} eventType 事件类型
     * @param {Function} handler 事件处理器 必须是引用传进来 使用对象引用相等判断
     */
    offHandler(eventType, handler) {
        const index = this.indexOf(eventType);
        if (index >= 0 && this.eventStacks[index].handlers.length) {
            // 存在,并且已经入栈
            const handlers = this.eventStacks[index].handlers;
            this.eventStacks[index].handlers = handlers.filter(currentHandler => {
                return currentHandler !== handler
            });
        }
    }
}

文件是基于Blob BlobMDN文档
Blob 类型可以使用slice截取一段 基于这点 我们就可以吧大文件拆分成很多小的文件块上传 前端实现如下:

import superagent from "superagent";
import eventEmitter from "../util/eventEmitter";

// 上传单个文件
export const uploadFile = (file, url) => {
    const formData = new FormData();
    formData.set("image", file);
    console.log(file.size);
    return superagent.post(url)
        .send(formData);
};

// 获取文件分块之后的数量
const getFragmentNum = (file, fragmentSize) => {
    // fragmentSize 拆分文件的大小
    return Math.ceil(file.size / fragmentSize);
};

// 获取文件碎片
const getFileFragment = (file, start, end, fragmentSize) => {
    return file.slice(start * fragmentSize, end * fragmentSize);
};

const getSlicedFiles = (file, fragmentSize) => {
    const slicedFiles = [];
    // 获取文件分块的数量
    const fragmentNum = getFragmentNum(file, fragmentSize);

    // 获取所有的分块文件
    for (let i = 0; i < fragmentNum; i++) {
        const currentFileFragment = getFileFragment(file, i, i + 1, fragmentSize);
        slicedFiles.push(currentFileFragment);
    }

    return slicedFiles;
};

// 多文件上传
export class uploadBigFile extends eventEmitter {
    constructor(file, url, fragmentSize) {
        super();

        // 拆分的文件数组
        const slicedFiles = getSlicedFiles(file, fragmentSize);
        // 正在上传的模块
        this.currentIndex = 0;
        this.uploadSlicedFiles(slicedFiles, url);
    };

    /**
     * 上传拆分后的文件
     *
     * @param {Array} files 文件数组
     * @param {string} url 上传的地址
     */
    uploadSlicedFiles(files, url) {
        uploadFile(files[this.currentIndex], url)
            .then(({body}) => {
                this.emit("process", {
                    currentIndex: this.currentIndex,
                    res: body,
                    progress: (this.currentIndex + 1) / files.length
                });

                this.currentIndex++;
                if (this.currentIndex < files.length) {
                    this.uploadSlicedFiles(files, url);
                }
            })
            .catch(err => {
                console.log(err);
            });
    };
}

后端实现如下:

const express = require("express");
const router = express.Router();
const multer = require("multer");
const upload = multer();

// multer用于接受formdata
router.post("/upload", upload.single("image"), (req, res) => {
    res.send({
        code: 0,
        file: req.file.size
    });
});

module.exports = router;

效果如图 :

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105510.html

相关文章

  • Node+H5实现文件分片上传(有源码)

    摘要:话前上传大文件上传的教程网上很多但是大部分没给出一个比较完整的出来这个博客给出的是前后端一套完整的解决方案其中前端没有使用第三方上传库希望能帮到有同样需求的朋友们大文件分片上传的好处在这里就不用多说了之前不管是上传单文件还是分片文件上传都是 话前 上传大文件上传的教程网上很多, 但是大部分没给出一个比较完整的出来, 这个博客给出的是前后端一套完整的解决方案, 其中前端没有使用第三方上传...

    1treeS 评论0 收藏0
  • web uploader 上传文件总结

    摘要:大文件上传主要分为三部分,预上传,分块上传,合并上传。可以扩展此对象来控制上传头部。是完成最终的大文件合并上传。修改可以控制发送哪些携带数据。 由于业务需要,需要上传大文件,已有的版本无法处理IE版本,经过调研,百度的 webuploader 支持 IE 浏览器,而且支持计算MD5值,进而可以实现秒传的功能。 大文件上传主要分为三部分,预上传,分块上传,合并上传。 预上传:计算MD5值...

    zhiwei 评论0 收藏0
  • PHP文件分片上传

    摘要:分片上传主要是前端将一个较大的文件分成等分的几片,标识当前分片是第几片和总共几片,待所有的分片均上传成功的时候,在后台进行合成文件即可。 一、前言 在网站开发中,经常会有上传文件的需求,有的文件size太大直接上传,经常会导致上传过程中耗时太久,大量占用带宽资源,因此有了分片上传。 分片上传主要是前端将一个较大的文件分成等分的几片,标识当前分片是第几片和总共几片,待所有的分片均上传成...

    tommego 评论0 收藏0
  • 关于文件上传

    showImg(https://segmentfault.com/img/bVbs1lu?w=675&h=221); 关于大文件上传 思路 使用js读取form表单中选择的file,计算文件md5值,并上传md5值到服务端,检查文件是否已上传过(类似秒传功能) 若文件未上传过,按照其大小切成1MB大小的块,小于1MB的不用切 用ajax异步提交切好的块上传至服务端(一个块一个请求,不阻塞,多线程...

    shusen 评论0 收藏0
  • 关于文件上传

    showImg(https://segmentfault.com/img/bVbs1lu?w=675&h=221); 关于大文件上传 思路 使用js读取form表单中选择的file,计算文件md5值,并上传md5值到服务端,检查文件是否已上传过(类似秒传功能) 若文件未上传过,按照其大小切成1MB大小的块,小于1MB的不用切 用ajax异步提交切好的块上传至服务端(一个块一个请求,不阻塞,多线程...

    zollero 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<