zoukankan      html  css  js  c++  java
  • 记录: 百度webuploader 分片文件上传java服务器端(spring mvc)示例的优化

    最近项目上用到文件分片上传,于是找到了百度的一个开源前端控件webuploader。 于是尝试使用。

    下载下来后,它提供的服务器端示例代码是php版的,那么Java版的呢?

    其实,上传文件都是按照rfc1867标注来的, 只是分段上传需要在前端多做点事情。分段上传原理其实就是在前端使用JavaScript对文件进行分割成不同小块,然后每次ajax请求就post一小块,直到全部收到为止。

    但是,为了确保后端能判断文件是否完整的收到,需要得知当前是第几块,一共多少块,每个分段的大小是多少(前后端同学约定好吧),webuploader是有把这些参数传给后端, 但文档里没说明参数名称啊,看了其他后端example,并且自己做了实验才知道。


    参数:
    name 文件名
    chunks 一共有多少分片
    chunk 当前分片是第几片file 文件对象
    重点来了:后端接收到这些参数应该怎么处理? 之前看过一个example ,是把每个分片文件都暂时存起来,命名为 文件名.part_1, 文件名.part_2, 文件名.part_3...文件名part_n, 每次都从1到总分片个数(注:这个值就是chunks的值),
    遍历这些文件是否存在.如果都存在说明全部上传完成了,则再循环一遍,把所有分段都合并到一个文件里. 这么做虽然是可以,但是如果文件很大, 最后一个分片到达的时候响应可能会很慢,效率低下.
    那么应该怎么解决呢?考虑了一会, 联想到,以前使用迅雷之类的工具下载,除下载下来的文件以外,还会有一个额外的文件用来存放下载之类的信息.
    受到这个启发, 我决定这么设计: 每当第n个分片到达时(注:n的值其实就是收到的chunk的值), 使用java的随机文件读写类RandomAccessFile, 定位到 n*分片大小(注:每个分片大小跟前端约定好的)的位置

    long offset = chunkSize * param.getChunk();
    //定位到该分片的偏移量
    accessTmpFile.seek(offset);

    然后写入分片内容.
    //写入该分片数据
    accessTmpFile.write(param.getFileItem().get());

    同时,往一个配置文件,暂且命名为 文件名.conf 设置长度为chunks的值, 也就是分片个数.

    accessConfFile.setLength(param.getChunks());

    然后往第n个位置写入一个Byte.MAX_VALUE,

    accessConfFile.seek(param.getChunk());
    accessConfFile.write(Byte.MAX_VALUE);

    因为写入的单位就是字节,所以我这么操作就相当于在第n个字节里写入全1的状态. 然后检查,从0到chunks开始每一个字节进行与操作, 一旦到第n个字节发现与运算的结果不是全1(Byte.MAX_VALUE)那么就说明这个文件的第n个部分没有传输完成.
    如果conf文件0到chunks的位置全部进行与运算的最后结果还是Byte.MAX_VALUE,那么就说明这个文件已经传输完成,该干嘛就干嘛..
    //completeList 检查是否全部完成,如果数组里是否全部都是(全部分片都成功上传)
    byte[] completeList = FileUtils.readFileToByteArray(confFile);
    byte isComplete = Byte.MAX_VALUE;
    for (int i = 0; i < completeList.length && isComplete==Byte.MAX_VALUE; i++) {
    //与运算, 如果有部分没有完成则 isComplete 不是 Byte.MAX_VALUE
    isComplete = (byte)(isComplete & completeList[i]);
    System.out.println(prefix + "check part " + i + " complete?:" + completeList[i]);
    }

    if (isComplete == Byte.MAX_VALUE) {
    System.out.println(prefix + "upload complete !!");
    }
    其实还有另一种想法是,前端传来该文件的md5码,然后后端每次接收到都算一次md5码,如果一致则说明上传成功,但是效率应该也不够上面的好,于是没实现. 谁有更好的想法可以留言哈.
     


    现在可以开始例子:
    在前端webuploader source的examples/image-upload/upload.js 中可以看到
            // 实例化
            uploader = WebUploader.create({
                pick: {
                    id: '#filePicker',
                    label: '点击选择图片'
                },
                formData: {
                    uid: 123
                },
                dnd: '#dndArea',
                paste: '#uploader',
                swf: '../../dist/Uploader.swf',
                chunked: false,
                chunkSize: 512 * 1024,
                server: '../../server/fileupload.php',
                // runtimeOrder: 'flash',
    
                // accept: {
                //     title: 'Images',
                //     extensions: 'gif,jpg,jpeg,bmp,png',
                //     mimeTypes: 'image/*'
                // },
    
                // 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
                disableGlobalDnd: true,
                fileNumLimit: 300,
                fileSizeLimit: 200 * 1024 * 1024,    // 200 M
                fileSingleSizeLimit: 50 * 1024 * 1024    // 50 M
            });
    chunked 被设置为false, 改为true就可以分片上传了。
    chunkSize这个后端需要用到,所以前后端需要保持一致。
    server改成java后端自己定义的上传文件接口的地址,我这里根据后端例子改成了“http://127.0.0.1:8080/file/test-upload2”

    ////此处已删除一些旧的不可运行的代码, 代码请看分割线下面

     ----------------------------------------2018 分割线--------------------------------------------------

    上面的代码太久远了, 从之前那公司项目里扣下来的, 忘记去掉某些不需要的类了` 在此抱歉

    新整理的代码放到github上了

    https://github.com/ThomasHuang025/webuploader-spring-example

  • 相关阅读:
    ABP.NET 方法使用 HTTPPOST,HTTPGET,HTTPPUT 特性
    Asp.Net Core 获取配置系统的链接字符串
    ABP.NET CORE 框架 取消新增用户邮箱地址必填验证
    前端开发常用组件库
    微信RSA加密公钥API
    动手实现一个较为简单的MQTT服务端和客户端
    计算入职时间的时间算法
    Windows Terminal 安装及美化
    Windows10内置Linux子系统(WSL)安装
    分享powershell设定网卡,ip,网关,dns的命令
  • 原文地址:https://www.cnblogs.com/drwong/p/5887348.html
Copyright © 2011-2022 走看看