基于Vue和springboot实现大文件,大图片分片上传

1.前端代码:

<template>
    <div>

        <div class="container">
            <el-upload
                    action
                    class="upload-demo"
                    drag
                    multiple
                    :http-request="httpRequestHandle"
            >
                <i class="el-icon-upload"></i>
                <div class="el-upload__text">
                    将文件拖到此处,或
                    <em>点击上传</em>
                </div>
                <template #tip>
                    <div class="el-upload__tip">只能上传 jpg/png 文件,且不超过 500kb</div>
                </template>
            </el-upload>

        </div>
    </div>
</template>

<script>
    import SparkMD5 from 'spark-md5'
    import ajax from 'axios';

    export default {
        name: "upload",
        data() {
            return {
                chunks: 0,
                chunkSize: 1024 * 1024,
            };
        },
        methods: {
            // 修改时间+文件名称+最后修改时间-->MD5
            md5File(file) {
                return new Promise((resolve, reject) => {
                    let blobSlice =
                        File.prototype.slice ||
                        File.prototype.mozSlice ||
                        File.prototype.webkitSlice
                    let chunkSize = file.size / 100
                    let chunks = 100
                    let currentChunk = 0
                    let spark = new SparkMD5.ArrayBuffer()
                    let fileReader = new FileReader()
                    fileReader.onload = function (e) {
                        console.log('read chunk nr', currentChunk + 1, 'of', chunks)
                        spark.append(e.target.result) // Append array buffer
                        currentChunk++
                        if (currentChunk < chunks) {
                            loadNext()
                        } else {
                            let cur = +new Date()
                            let result = spark.end()
                            resolve(result)
                        }
                    }
                    fileReader.onerror = function (err) {
                        reject(err)
                    }

                    function loadNext() {
                        let start = currentChunk * chunkSize
                        let end = start + chunkSize >= file.size ? file.size : start + chunkSize
                        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
                    }

                    loadNext()
                })
            },
            // 校验文件的MD5
            checkFileMD5(file, fileName, fileMd5Value, onError) {
                const fileSize = file.size
                const {chunkSize} = this
                this.chunks = Math.ceil(fileSize / chunkSize)
                return new Promise(async (resolve, reject) => {
                    let formData = new FormData();
                    formData.append("fileName", fileName);
                    formData.append("path", fileMd5Value);
                    const result = await ajax({
                        url: 'common/upload/checkFile',
                        method: "POST",
                        baseURL: "http://localhost:8080",
                        data: formData
                    })
                    resolve(result)
                })
            },
            //文件分片
            async checkAndUploadChunk(file, fileMd5Value, chunkList) {
                let {chunks, upload} = this
                const requestList = []
                for (let i = 0; i < chunks; i++) {
                    requestList.push(upload(i, fileMd5Value, file))
                }
                console.log({requestList})
                const result =
                    requestList.length > 0
                        ? await Promise.all(requestList)
                            .then(result => {
                                console.log({result})
                                //return result.every(i => i.ok)
                                return true;
                            })
                            .catch(err => {
                                return err
                            })
                        : true
                console.log({result})
                return result === true
            },

            // 上传chunk
            upload(i, fileMd5Value, file) {
                const {chunks} = this
                return new Promise((resolve, reject) => {
                    let {chunkSize} = this
                    // 构造一个表单,FormData是HTML5新增的
                    let end = (i + 1) * chunkSize >= file.size ? file.size : (i + 1) * chunkSize
                    let form = new FormData()
                    form.append('file', file.slice(i * chunkSize, end)) // file对象的slice方法用于切出文件的一部分
                    form.append('total', chunks) // 总片数
                    form.append('index', i) // 当前是第几片
                    form.append('path', fileMd5Value)

                    ajax({
                        url: 'common/upload/uploadFile',
                        method: "POST",
                        baseURL: "http://localhost:8080",
                        data: form
                    }).then(data => {
                        resolve(data)
                    })
                })
            },


            async httpRequestHandle(data) {
                let fileMd5Value = await this.md5File(data.file)
                let uploaded = await this.checkFileMD5(data.file, data.file.name, fileMd5Value)
                await this.checkAndUploadChunk(data.file, fileMd5Value);

                var formData = new FormData();
                formData.append("name", data.file.name);
                formData.append("path", fileMd5Value);
                ajax({
                    url: 'common/upload/mergeFile',
                    method: "POST",
                    baseURL: "http://localhost:8080",
                    data: formData
                })
            }
        }
    };
</script>


2.后端代码:

package com.lz.hehuorenservice.controller.common;

import com.lz.hehuorenservice.common.bean.Project;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

/** Create by hyhweb on 2021/6/1 18:01 */
@RestController
@Api(tags = "上传接口")
@RequestMapping("common")
public class UploadController {

  @Autowired 
  private Project project;

  @ApiOperation("上传图片接口")
  @PostMapping("/upload/uploadFile")
  public String upload(
      @RequestParam("file") MultipartFile file,
      @RequestParam("path") String path,
      @RequestParam("index") String index,
      @RequestParam("total") String total)
      throws Exception {
    if (file.isEmpty()) {
      return "请选择要上传的文件";
    }
    byte[] bytes = file.getBytes();
    String fileName = file.getOriginalFilename();
    Path uploadPath = Paths.get(project.getUploadUrl() + path + "/" + index);

    File files = new File(project.getUploadUrl() + path);
    if (!files.exists()) {
      files.mkdirs();
    }
    Files.write(uploadPath, bytes);
    return "上传成功";
  }

  @PostMapping("/upload/checkFile")
  public Map checkFile(@RequestParam("path") String path) throws Exception {
    String uploadPath = project.getUploadUrl() + path;
    File file = new File(uploadPath);
    if (file.exists() && file.isDirectory()) {
      String[] fileList = file.list();
      Map map = new HashMap();
      map.put("list", fileList);
      map.put("message", "文件已经存在");
      return map;
    } else {
      Map map = new HashMap();
      file.mkdirs();
      map.put("message", "文件不存在,已经重新创建");
      return map;
    }
  }

  @PostMapping("/upload/mergeFile")
  public String mergeFile(@RequestParam("path") String path, @RequestParam("name") String name)
      throws Exception {
    String uploadPath = project.getUploadUrl() + path;
    File file = new File(uploadPath);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    if (file.exists()) {
      String[] fileList = file.list();
      int lengthByte = 0;
      for (String url : fileList) {
        InputStream in = new FileInputStream(uploadPath + "/" + url);
        lengthByte += in.available();
      }
      byte[] bytes = new byte[lengthByte];
      int countLength = 0;
      for (String url : fileList) {
        InputStream in = new FileInputStream(uploadPath + "/" + url);
        byte[] b = toByteArray(in);
        System.arraycopy(b, 0, bytes, countLength, b.length);
        countLength += b.length;
        in.close();
      }
      Path savePath = Paths.get(uploadPath + "-" + name);
      Files.write(savePath, bytes);
    }
    return "合并文件成功";
  }

  private static byte[] byteMerger(byte[]... byteList) {
    int lengthByte = 0;
    for (int i = 0; i < byteList.length; i++) {
      lengthByte += byteList[i].length;
    }
    byte[] allByte = new byte[lengthByte];
    int countLength = 0;
    for (int i = 0; i < byteList.length; i++) {
      byte[] b = byteList[i];
      System.arraycopy(b, 0, allByte, countLength, b.length);
      countLength += b.length;
    }
    return allByte;
  }

  private byte[] toByteArray(InputStream in) throws IOException {

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024 * 1024];
    int n = 0;
    while ((n = in.read(buffer)) != -1) {
      out.write(buffer, 0, n);
    }
    return out.toByteArray();
  }
}

3.实现效果,如下:

先分片上传文件,最后合并成一个文件
在这里插入图片描述
上传到服务器的分片文件,不需要文件后缀,到时候以流的方式读取出来合并文件就好
在这里插入图片描述
在这里插入图片描述
文件打开正常,如下:
在这里插入图片描述

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值