前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue-simple-uploader之后端(ssm)实现(补坑记)

vue-simple-uploader之后端(ssm)实现(补坑记)

作者头像
陨石坠灭
发布2020-01-21 15:58:38
2.2K0
发布2020-01-21 15:58:38
举报
文章被收录于专栏:全栈之路全栈之路

实现说一下心得:太坑了,接下来就让我描述一下我的填坑之路,顺便附上代码。

坑的地方说一下,以免读者错过精彩部分:要按照vue-simple-uploader的要求实现断点续传!!!那么普通的文件上传以及博客上的断点续传就只能借鉴,无法抄袭,坑!!!!

环境说明

前端 - 工具:VSCode - 框架:vue

后端 - IDE: eclipse - 开发语言:java - 框架:ssm、maven

资源说明

vue-uploader: https://github.com/simple-uploader/vue-uploader

操作

vue-uploader加入前端项目

代码语言:javascript
复制
npm install vue-simple-uploader --save

然后配置main.js

代码语言:javascript
复制
import Vue from 'vue'
import uploader from 'vue-simple-uploader'
import App from './App.vue'
...
Vue.use(uploader)
...

使用

代码语言:javascript
复制
<template>
  <uploader :options="options" class="uploader-example">
    <uploader-unsupport></uploader-unsupport>
    <uploader-drop>
      <p>Drop files here to upload or</p>
      <uploader-btn>select files</uploader-btn>
      <uploader-btn :attrs="attrs">select images</uploader-btn>
      <uploader-btn :directory="true">select folder</uploader-btn>
    </uploader-drop>
    <uploader-list></uploader-list>
  </uploader>
</template>

<script>
  export default {
    data () {
      return {
        options: {
          //nodejs服务器代码 https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js 
          target: '//localhost:8080/example/test/upload'
        },
        attrs: {
          accept: 'video/*'
        }
      }
    }
  }
</script>

<style>
...

然后就是服务器代码,创建TestController,添加upload方法

代码语言:javascript
复制
...
@Controller
@RequestMapping("test/")
public class TestController{
...
@ResponseBody
@RequestMapping(path = "/upload", method = RequestMethod.POST)
  public void upload(HttpServletRequest request, HttpServletResponse response){
    UploadUtils.upload(request);
    this.success(response, "上传成功!");
  }
...
}

其中success方法

代码语言:javascript
复制
...
@Controller
@RequestMapping("test/")
public class TestController{
...
  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();
      }
    }
  }
...

}

UploadUtils内容

代码语言:javascript
复制
...
public class UploadUtils{
  public void  upload(HttpServletRequest request){
        String path = "E:/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;
	}
  }
}

按照道理,这里就应该讲完了,但是,但是!上传超过10M的文件就会有问题,原因是你没有实现断点续传!!!

关键是你知道了,也无从下手,不过还有提供了Nodejs版的服务端代码,就当了一回翻译君

Nodejs版代码: https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js

接下来就是本人写的Uploader类了,对应的js代码是uploader-node.js

额,代码太多,就先放在最后面了。

先讲一下用法吧。

使用
  • 定义service
代码语言:javascript
复制
@Service
public UploadService extends Uploader{
  //什么也不写
}
  • 调用
代码语言:javascript
复制
@Controller
@RequestMapping("test/")
public class TestController{

@Autowired
private UploadService uploadService;
...
@ResponseBody
@RequestMapping(path = "/upload", method = RequestMethod.POST)
  public void upload(HttpServletRequest request, HttpServletResponse response){
      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){
                      this.sucess(status);
                }
              }
        }
      }catch(Excetion e){
        e.printStackTrace();
      }
  }
...
}

最后,把success方法里边的write.close()给注释掉,不然会报错,具体原因还不知,但是坑定是与异步有关,这也是一大坑呐。。。

好了,改关门放代码了:

代码语言:javascript
复制
//Uploader.java


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;

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;

//https://github.com/simple-uploader/Uploader/blob/develop/samples/Node.js/uploader-node.js
/**
 * 断点续传
 * 
 * @author 陨石坠灭
 *
 */
public class Uploader {
	/**
	 * 临时文件夹
	 */
	private String temporaryFolder;
	/**
	 * 最大文件大小
	 */
	private Integer maxFileSize = 52428800;
	// 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, "uploader-" + 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,
								identifier + File.pathSeparator + 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;
	}
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/10/20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 环境说明
    • 操作
      • 使用
      相关产品与服务
      对象存储
      对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档