springboot + vue + vue-simple-uploader 实现大文件分片上传

原文章 https://www.cnblogs.com/hz-handofgod/p/11985170.html  适当修改

参考官方组件文档 https://github.com/simple-uploader/vue-uploader

目录

前台VUE

后台

效果图


前台VUE

 添加npm包

npm install vue-simple-uploader --save

 main.js

import upLoader from 'vue-simple-uploader'
Vue.use(upLoader)

vue页面

<template>
  <div>
    <div>
      <uploader
        :options="options"
        :file-status-text="statusText"
        class="uploader-example"
        ref="uploader"
        @file-success="fileSuccess"
        @file-error="fileError"
        style="width: 100%;"
      >
        <uploader-unsupport></uploader-unsupport>
        <uploader-drop>
          <p>上传界面</p>
          <uploader-btn single>选择文件</uploader-btn>
        </uploader-drop>
        <uploader-list></uploader-list>
      </uploader>
    </div>
  </div>
</template>

<script>
export default {
  name: 'UploadTest',
  data() {
    return {
      recording: {},
      options: {
        // https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js
        target: '/apis/upload/uploadChunk', //后台上传文件地址
        testChunks: false, //不校验
        chunkSize: 2048000 //每片大小
      },
      statusText: {
        success: '上传成功',
        error: '上传失败',
        uploading: '上传中',
        paused: '暂停中',
        waiting: '等待中'
      }
    }
  },
  methods: {
    //上传成功的回调事件
    fileSuccess(rootFile, file, message, chunk) {
      var name = message.split('\\')
      var nameLast = name[name.length - 1]
      // 文件链接
      this.recording.recordingUrl = 'http://localhost:8090/static/' + nameLast
      // 打印访问服务器的文件地址
      alert(this.recording.recordingUrl)
    },
    fileError() {

    }
  },
  mounted() {
    // 获取uploader对象
    this.$nextTick(() => {
      window.uploader = this.$refs.uploader.uploader
    })
  }
}
</script>

<style scoped>
.uploader-example {
  width: 100%;
  padding: 15px;
  margin: 50px auto 0;
  font-size: 12px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
}

.uploader-example .uploader-btn {
  margin-right: 4px;
}

.uploader-example .uploader-list {
  max-height: 440px;
  overflow: auto;
  overflow-x: hidden;
  overflow-y: auto;
}
</style>

关于上传选择文件的单选多选,uploader-btn标签的single属性控制

页面展示效果

后台

pom添加两个依赖

     <!--文件上传-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.1</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

util文件 Uploader

package com.cei.xyd_association.util;


import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.Iterator;

/**
 * 断点续传
 *
 * @author 
 */
public class Uploader {
    /**
     * 临时文件夹
     */
    private String temporaryFolder;
    /**
     * 最大文件大小 2GB
     */
    private Integer maxFileSize = 2048000000;
    // private String fileParameterName;

    public Uploader(String temporaryFolder, String fileParameterName) {
        this.temporaryFolder = temporaryFolder;
        File file = new File(temporaryFolder);
        if (!file.exists()) {
            file.mkdirs();
        }
        // if (fileParameterName == null) {
        // this.fileParameterName = "file";
        // } else {
        // this.fileParameterName = fileParameterName;
        // }
    }

    public String cleanIdentifier(String identifier) {
        return identifier.replaceAll("[^0-9A-Za-z_-]", "");
    }

    public String getChunkFilename(int chunkNumber, String identifier) {
        identifier = cleanIdentifier(identifier);
        return new File(temporaryFolder, "jwj-" + identifier + '-' + chunkNumber).getAbsolutePath();
    }

    public String validateRequest(int chunkNumber, int chunkSize, int totalSize, String identifier, String filename,
                                  Integer fileSize) {
        identifier = cleanIdentifier(identifier);

        if (chunkNumber == 0 || chunkSize == 0 || totalSize == 0 || identifier.length() == 0
                || filename.length() == 0) {
            return "non_uploader_request";
        }
        int numberOfChunks = (int) Math.max(Math.floor(totalSize / (chunkSize * 1.0)), 1);
        if (chunkNumber > numberOfChunks) {
            return "invalid_uploader_request1";
        }

        if (this.maxFileSize != null && totalSize > this.maxFileSize) {
            return "invalid_uploader_request2";
        }

        if (fileSize != null) {
            if (chunkNumber < numberOfChunks && fileSize != chunkSize) {
                return "invalid_uploader_request3";
            }
            if (numberOfChunks > 1 && chunkNumber == numberOfChunks
                    && fileSize != ((totalSize % chunkSize) + chunkSize)) {
                return "invalid_uploader_request4";
            }
            if (numberOfChunks == 1 && fileSize != totalSize) {
                return "invalid_uploader_request5";
            }
        }

        return "valid";
    }

    public int getParamInt(HttpServletRequest req, String key, int def) {
        String value = req.getParameter(key);
        try {
            return Integer.parseInt(value);
        } catch (Exception e) {
        }
        return def;
    }

    public String getParamString(HttpServletRequest req, String key, String def) {
        String value = req.getParameter(key);
        try {
            return value == null ? def : value;
        } catch (Exception e) {
        }
        return def;
    }

    public void get(HttpServletRequest req, UploadListener listener) {
        int chunkNumber = this.getParamInt(req, "chunkNumber", 0);
        int chunkSize = this.getParamInt(req, "chunkSize", 0);
        int totalSize = this.getParamInt(req, "totalSize", 0);
        String identifier = this.getParamString(req, "identifier", "");
        String filename = this.getParamString(req, "filename", "");
        if (validateRequest(chunkNumber, chunkSize, totalSize, identifier, filename, null).equals("valid")) {
            String chunkFilename = getChunkFilename(chunkNumber, identifier);
            if (new File(chunkFilename).exists()) {
                listener.callback("found", chunkFilename, filename, identifier, null);
            } else {
                listener.callback("not_found", null, null, null, null);
            }

        } else {
            listener.callback("not_found", null, null, null, null);
        }
    }

    public void post(HttpServletRequest req, UploadListener listener) throws IllegalStateException, IOException {

        int chunkNumber = this.getParamInt(req, "chunkNumber", 0);
        int chunkSize = this.getParamInt(req, "chunkSize", 0);
        int totalSize = this.getParamInt(req, "totalSize", 0);
        String identifier = this.getParamString(req, "identifier", "");
        String filename = this.getParamString(req, "filename", "");

        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(req.getSession().getServletContext());

        if (multipartResolver.isMultipart(req)) {
            // 将request变成多部分request
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) req;
            // 获取multiRequest 中所有的文件名
            Iterator<String> iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                String name = iter.next().toString();
                // if (!this.fileParameterName.equals(name)) {
                // continue;
                // }
                MultipartFile file = multiRequest.getFile(name);

                if (file != null && file.getSize() > 0) {
                    String original_filename = file.getOriginalFilename();
                    // String original_filename =
                    // files[this.fileParameterName]['originalFilename'];
                    String validation = validateRequest(chunkNumber, chunkSize, totalSize, identifier, filename,
                            (int) file.getSize());

                    if ("valid".equals(validation)) {
                        String chunkFilename = getChunkFilename(chunkNumber, identifier);

                        File f = new File(chunkFilename);
                        if (!f.exists()) {
                            file.transferTo(f);
                        }

                        int currentTestChunk = 1;
                        int numberOfChunks = (int) Math.max(Math.floor(totalSize / (chunkSize * 1.0)), 1);

                        currentTestChunk = this.testChunkExists(currentTestChunk, chunkNumber, numberOfChunks,
                                chunkFilename, original_filename, identifier, listener, "file");

                    } else {
                        listener.callback(validation, filename, original_filename, identifier, "file");
                    }
                } else {
                    listener.callback("invalid_uploader_request", null, null, null, null);
                }
            }
        }
    }

    private void pipeChunk(int number, String identifier, UploadOptions options, OutputStream writableStream)
            throws IOException {
        String chunkFilename = getChunkFilename(number, identifier);
        if (new File(chunkFilename).exists()) {
            FileInputStream inputStream = new FileInputStream(new File(chunkFilename));
            int maxlen = 1024;
            int len = 0;
            try {
                byte[] buff = new byte[maxlen];
                while ((len = inputStream.read(buff, 0, maxlen)) > 0) {
                    writableStream.write(buff, 0, len);
                }
            } finally {
                inputStream.close();
            }
            pipeChunk(number + 1, identifier, options, writableStream);
        } else {
            if (options.end)
                writableStream.close();
            if (options.listener != null)
                options.listener.onDone();
        }
    }

    public void write(String identifier, OutputStream writableStream, UploadOptions options) throws IOException {
        if (options == null) {
            options = new UploadOptions();
        }
        if (options.end == null) {
            options.end = true;
        }
        pipeChunk(1, identifier, options, writableStream);
    }

    /**
     * @param currentTestChunk
     * @param chunkNumber       当前上传块
     * @param numberOfChunks    总块数
     * @param filename          文件名称
     * @param original_filename 源文件名称
     * @param identifier        文件
     * @param listener          监听
     * @param fileType
     * @return
     */
    private int testChunkExists(int currentTestChunk, int chunkNumber, int numberOfChunks, String filename,
                                String original_filename, String identifier, UploadListener listener, String fileType) {
        String cfile = getChunkFilename(currentTestChunk, identifier);
        if (new File(cfile).exists()) {
            currentTestChunk++;
            if (currentTestChunk >= chunkNumber) {
                if (chunkNumber == numberOfChunks) {
                    try {
                        System.out.println("done");
                        // 文件合并
                        UploadOptions options = new UploadOptions();
                        File f = new File(this.temporaryFolder, original_filename.substring(0, original_filename.lastIndexOf("."))
                                + "-" + UUIDUtil.getUUID_36() + "." + FilenameUtils.getExtension(original_filename));
                        options.listener = new UploadDoneListener() {
                            @Override
                            public void onError(Exception err) {
                                listener.callback("invalid_uploader_request", f.getAbsolutePath(), original_filename,
                                        identifier, fileType);
                                clean(identifier, null);
                            }

                            @Override
                            public void onDone() {
                                listener.callback("done", f.getAbsolutePath(), original_filename, identifier, fileType);
                                clean(identifier, null);
                            }
                        };
                        this.write(identifier, new FileOutputStream(f), options);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                        listener.callback("invalid_uploader_request", filename, original_filename, identifier,
                                fileType);
                    } catch (IOException e) {
                        e.printStackTrace();
                        listener.callback("invalid_uploader_request", filename, original_filename, identifier,
                                fileType);
                    }
                } else {
                    listener.callback("partly_done", filename, original_filename, identifier, fileType);
                }
            } else {
                return testChunkExists(currentTestChunk, chunkNumber, numberOfChunks, filename, original_filename,
                        identifier, listener, fileType);
            }
        } else {
            listener.callback("partly_done", filename, original_filename, identifier, fileType);
        }
        return currentTestChunk;
    }

    public void clean(String identifier, UploadOptions options) {
        if (options == null) {
            options = new UploadOptions();
        }
        pipeChunkRm(1, identifier, options);
    }

    private void pipeChunkRm(int number, String identifier, UploadOptions options) {

        String chunkFilename = getChunkFilename(number, identifier);
        File file = new File(chunkFilename);
        if (file.exists()) {
            try {
                file.delete();
            } catch (Exception e) {
                if (options.listener != null) {
                    options.listener.onError(e);
                }
            }
            pipeChunkRm(number + 1, identifier, options);

        } else {
            if (options.listener != null)
                options.listener.onDone();
        }
    }

    public static interface UploadListener {
        public void callback(String status, String filename, String original_filename, String identifier,
                             String fileType);
    }

    public static interface UploadDoneListener {
        public void onDone();

        public void onError(Exception err);
    }

    public static class UploadOptions {
        public Boolean end;
        public UploadDoneListener listener;
    }
}

这里面需要改两个地方

maxFileSize 设置上传的文件大小最大限制

UUIDUtil.getUUID_36()  这个是我项目自己的UUID随机生成的工具类方法,自己弄一套生成就行

util文件 UploadUtils

package com.cei.xyd_association.util;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

public class UploadUtils {

    public String upload(HttpServletRequest request) {
        String path = "F:/tmp/";
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        if (multipartResolver.isMultipart(request)) {
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
            Iterator<String> iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                // 一次遍历所有文件
                MultipartFile file = multiRequest.getFile(iter.next().toString());
                if (file != null) {
                    String p = path + "/" + file.getOriginalFilename();
                    // 上传
                    try {
                        file.transferTo(new File(p));
                        return p;
                    } catch (IllegalStateException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }
}

自己改下自己的path就行 

controller

package com.cei.xyd_association.web;

import com.cei.xyd_association.service.impl.UploadService;
import com.cei.xyd_association.util.UploadUtils;
import com.cei.xyd_association.util.Uploader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

@RestController
@RequestMapping("/upload")
public class uploadController {

    @Resource
    private UploadService uploadService;

    /**
     * 分片上传
     *
     * @param request  前台发送过来的文件内容
     * @param response 响应给前台文件路径
     * @throws Exception
     */
    @RequestMapping(value = "/uploadChunk", method = RequestMethod.POST)
    public void upload2(HttpServletRequest request, HttpServletResponse response) throws Exception {
        final String[] filepath = {""};
        final String[] or_filename = {""};
        try {
            uploadService.post(request, new Uploader.UploadListener() {
                @Override
                public void callback(String status, String filename, String original_filename, String identifier, String fileType) {
                    if (status != null) {
                        System.out.println(filename);
                    }
                    filepath[0] = filename;//文件上传的路径带走
                    or_filename[0] = original_filename;//源文件名称带走
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        //这句话的意思,是让浏览器用utf8来解析返回的数据
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        //这句话的意思,是告诉servlet用UTF-8转码,而不是用默认的ISO8859
        response.setCharacterEncoding("UTF-8");
        response.getWriter().append(filepath[0]);
    }

    @ResponseBody
    @RequestMapping(path = "/upload2", method = RequestMethod.POST)
    public void upload(HttpServletRequest request, HttpServletResponse response) {
        new UploadUtils().upload(request);
        this.success(response, "上传成功!");
    }


    public void success(HttpServletResponse response, Object obj) {
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.write(obj.toString());
        } catch (Exception e) {
        } finally {
            if (writer != null) {
                //writer.close();
            }
        }
    }
}

这里我把 response.getWriter().append(filepath[0]); 改了,因为我们需求返回给前台能访问服务器的文件路径,所以返回文件名方便拼地址

 Service文件 UploadService

package com.cei.xyd_association.service.impl;


import com.cei.xyd_association.util.Uploader;
import org.springframework.stereotype.Service;

@Service
public class UploadService extends Uploader {
    public UploadService(){
        super("F:/tmp/","file");
    }
}

还是改自己存文件的地方

访问映射

application添加

#上传文件的路径和映射地址
#本地存放路径
localFileUrl: F:\tmp
#映射的网络url
mappingFileUrl: /static/

 application的server设置

 

 访问本地资源的启动配置

package com.cei.xyd_association.config;

import com.cei.xyd_association.Interceptor.TESTInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class MyWebAppConfiguration extends WebMvcConfigurerAdapter {

    @Value("${localFileUrl}")
    private String localFileUrl;//本地存放的文件路径 例如:E:\down
    @Value("${mappingFileUrl}")
    private String mappingFileUrl;//映射的虚拟网络地址  供页面连接访问的url

    @Bean
    public TESTInterceptor getSessionInterceptor() {
        return new TESTInterceptor();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(mappingFileUrl+"**").addResourceLocations("file:"+localFileUrl+"/");
    }

    /**
     * 拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getSessionInterceptor())
                .addPathPatterns("/**");
    }

}

这个TESInterceptor是我自己的拦截器啊,实际里面没写东西,用自己的拦截器就行,我只是把代码全粘出来了没筛选

package com.cei.xyd_association.Interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class TESTInterceptor implements HandlerInterceptor {

    /**
     * 日志对象
     */
    protected static Logger logger = LoggerFactory.getLogger(TESTInterceptor.class);


    /**
     * 在controller后拦截
     */
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception exception) throws Exception {

    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 在controller前拦截
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
        return true;
    }
}

效果图

前台切分片

 后台上传分片

 分片文件

 最后传完合并

前台alert出文件在服务器的地址

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值