完整的多文件上传实例(java版)
昨天刚刚做了一个文件列表上传,后端很简单,用
- MultipartFile[] files
获取文件流数组,后端就当IO流操作就可以,似乎好像没啥好写的,但是!!!!!前端是真的糙单.要是自己写一个前端单个文件上传样式是丑了点,不过还是能用的,只是样式是真的丑了....无语了,所有有了这篇.首先来张完成的效果
下面就是实现步骤了,开始对比了Bootstrap fileinput 和jQeury的uploadfile,我使用的功能似乎单一且简单,所以并不需要哪些花狸狐哨的功能,所以选择了这个插件,首先还是感谢大佬,开源这么好的插件
- DEMO地址: http://w.twinkling.cn/
- 官网地址: http://www.twinkling.cn/
我使用的是SpringBoot,上面的demo是基于基本的servlet写的,现在需要整合到我的项目中.
这个需要注意一点,插件需要写一个
- /tk 请求,用于生层上传文件的唯一TOKEN,标识文件,其他的还需要一个配置类,基本配置文件,涉及到文件上传的一些配置,等下一起给出来
前端代码:
- <head>
- <meta charset="UTF-8">
- <title>上传数据</title>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <link href="../../static/files/css/stream-v1.css" rel="stylesheet" type="text/css">
- </head>
- <style>
- body{
- width: 90%;
- padding: 10px;
- margin-left: 5%;
- }
- #i_select_files{
- width: 70%;
- height: 5rem;
- }
- #i_stream_message_container{
- position: absolute !important;
- float: left !important;
- }
- #operate{
- position: absolute;
- width: 50%;
- height: 100%;
- }
- #result{
- position: absolute;
- width: 50%;
- height: 100%;
- margin-left: 50%;
- }
- button{
- background: transparent;
- border: 1px #2a6496 solid;
- width: 3rem;
- margin-left: 2.8rem;
- margin-top: 2rem;
- border-radius: 5px;
- background-color: #0099FF;
- font-weight: bolder;
- }
- button:hover{
- background-color: #4cae4c;
- pointer-events: painted;
- }
- #i_select_files div{
- margin-top: 1rem;
- }
- </style>
- <body>
- <div id="operate">
- <div id="i_select_files"></div>
- <div id="i_stream_files_queue"></div>
- <button onclick="javascript:_t.upload();">开始上传</button>
- <button onclick="javascript:_t.stop();">停止上传</button>
- <button onclick="javascript:_t.cancel();">取消上传</button>
- <button onclick="javascript:_t.destroy();_t=null;_t=new Stream(config);">重新上传</button>
- <button id="import" style="color: #c9302c">导入文件</button>
- </div>
- <div id="result">
- 结果信息:
- <div id="i_stream_message_container" class="stream-main-upload-box" style="overflow: auto;height: 93%;color: #3c763d"></div>
- </div>
- </body>
- <script type="text/javascript" src="../../static/js/jquery-1.11.3.min.js"></script>
- <script type="text/javascript" src="../../static/files/js/stream-v1.js"></script>
- <script src="https://cdn.bootcss.com/layer/2.3/layer.js"></script>
- <script type="text/javascript">
- $("#import").click(function () {
- //询问框
- layer.confirm('确定导入这个月最新上传的数据?', {
- btn: ['确定','我在想想'] //按钮
- }, function(){
- //加载层
- var index = layer.load(0, {shade: false}); //0代表加载的风格,支持0-2
- $.ajax({
- type: "GET",
- url: "/xlxs/setData",
- success: function (data) {
- if(data != null){
- layer.close(index);
- layer.alert('导入成功', {
- skin: 'layui-layer-lan'
- ,closeBtn: 0
- ,anim: 4 //动画类型
- });
- }else{
- layer.msg("导入失败,请重新导入!");
- }
- }
- });
- }, function(){
- });
- })
- /**
- * 配置文件(如果没有默认字样,说明默认值就是注释下的值)
- * 但是,on*(onSelect, onMaxSizeExceed...)等函数的默认行为
- * 是在ID为i_stream_message_container的页面元素中写日志
- */
- var config = {
- browseFileId : "i_select_files", /** 选择文件的ID, 默认: i_select_files */
- browseFileBtn : "<div>请把xlxs文件拖到这里</div>", /** 显示选择文件的样式, 默认: <div>请选择文件</div> */
- dragAndDropArea: "i_select_files", /** 拖拽上传区域,Id(字符类型"i_select_files")或者DOM对象, 默认: `i_select_files` */
- dragAndDropTips: "<span>(文件夹)也是可以的</span>", /** 拖拽提示, 默认: <span>把文件(文件夹)拖拽到这里</span> */
- filesQueueId : "i_stream_files_queue", /** 文件上传容器的ID, 默认: i_stream_files_queue */
- filesQueueHeight : 200, /** 文件上传容器的高度(px), 默认: 450 */
- messagerId : "i_stream_message_container", /** 消息显示容器的ID, 默认: i_stream_message_container */
- multipleFiles: true, /** 多个文件一起上传, 默认: false */
- onRepeatedFile: function(f) {
- alert("文件:"+f.name +" 大小:"+f.size + " 已存在于上传队列中。");
- return false;
- },
- autoUploading: false, /** 选择文件后是否自动上传, 默认: true */
- autoRemoveCompleted : true, /** 是否自动删除容器中已上传完毕的文件, 默认: false */
- maxSize: 20480000, /** 单个文件的最大大小,默认:2G */
- retryCount : 3, /** HTML5上传失败的重试次数 */
- // postVarsPerFile : { /** 上传文件时传入的参数,默认: {} */
- // param1: "val1",
- // param2: "val2"
- // },
- // swfURL : "/swf/FlashUploader.swf", /** SWF文件的位置 */
- // tokenURL : "/tk", /** 根据文件名、大小等信息获取Token的URI(用于生成断点续传、跨域的令牌) */
- // frmUploadURL : "/fd;", /** Flash上传的URI */
- uploadURL : "/upload", /** HTML5上传的URI */
- simLimit: 50, /** 单次最大上传文件个数 */
- extFilters: [".xlsx"], /** 允许的文件扩展名, 默认: [] */
- // onSelect: function(list) {alert('onSelect')}, /** 选择文件后的响应事件 */
- onMaxSizeExceed: function(size, limited, name) {
- alert("上传文件太大了,支持20MB以下")
- }, /** 文件大小超出的响应事件 */
- onFileCountExceed: function(selected, limit) {
- alert("最大上传数量是50个");
- }, /** 文件数量超出的响应事件 */
- onExtNameMismatch: function(name, filters) {
- alert(file.name+' 的文件格式不对,换个试试[xlsx]')
- }, /** 文件的扩展名不匹配的响应事件 */
- // onCancel : function(file) {
- //
- // }, /** 取消上传文件的响应事件 */
- // onComplete: function(file) {alert('onComplete')}, /** 单个文件上传完毕的响应事件 */
- onQueueComplete: function() {
- _t.destroy();_t=null;_t=new Stream(config);
- }, /** 所有文件上传完毕的响应事件 */
- onUploadError: function(status, msg) {
- alert('上传失败')
- }, /** 文件上传出错的响应事件 */
- onDestroy: function() {
- } /** 文件上传出错的响应事件 */
- };
- var _t = new Stream(config);
- </script>
这里面需要获取两个文件
- stream-v1.css
- stream-v1.js
其实这个也好获取,要是大家拿不到的话,我就发出来,其他的例子上说明的很详细,按照自己的业务要求修改就可以了.
后台代码:
这里是按照官网给的案例,整合到自己的SpringBoot项目中的,只是稍微修改了下代码,就可以了,只是需要找到修改的地方即可,要是找不到,呵呵,那就又要花费一天的干活.
好啦,开始....基于MVC模式
Controller
- @GetMapping("/upload")
- public void getUpload(HttpServletRequest request,
- HttpServletResponse response) throws IOException, ServletException {
- StreamServlet streamServlet = new StreamServlet();
- streamServlet.doGet(request,response);
- }
- @PostMapping("/upload")
- public void postUpload(HttpServletRequest request,
- HttpServletResponse response) throws IOException, ServletException {
- StreamServlet streamServlet = new StreamServlet();
- streamServlet.doPost(request,response);
- }
- streamServlet:
- public class StreamServlet extends HttpServlet {
- private static final long serialVersionUID = -8619685235661387895L;
- /** when the has increased to 10kb, then flush it to the hard-disk. */
- static final int BUFFER_LENGTH = 10240;
- static final String START_FIELD = "start";
- public static final String CONTENT_RANGE_HEADER = "content-range";
- /**
- * Lookup where's the position of this file?
- */
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- doOptions(req, resp);
- final String token = req.getParameter(UploadController.TOKEN_FIELD);
- final String size = req.getParameter(UploadController.FILE_SIZE_FIELD);
- final String fileName = req.getParameter(UploadController.FILE_NAME_FIELD);
- final PrintWriter writer = resp.getWriter();
- /** TODO: validate your token. */
- JSONObject json = new JSONObject();
- long start = 0;
- boolean success = true;
- String message = "";
- try {
- File f = IoUtil.getTokenedFile(token);
- start = f.length();
- } finally {
- try {
- if (success)
- json.put(START_FIELD, start);
- json.put(UploadController.SUCCESS, success);
- json.put(UploadController.MESSAGE, message);
- } catch (JSONException e) {}
- writer.write(json.toString());
- IoUtil.close(writer);
- }
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- doOptions(req, resp);
- final String token = req.getParameter(UploadController.TOKEN_FIELD);
- final String fileName = req.getParameter(UploadController.FILE_NAME_FIELD);
- Range range = IoUtil.parseRange(req);
- OutputStream out = null;
- InputStream content = null;
- final PrintWriter writer = resp.getWriter();
- //清除旧的文件
- if(!IoUtil.deleteFile(fileName)){
- writer.write("上传失败");
- }
- /** TODO: validate your token. */
- JSONObject json = new JSONObject();
- long start = 0;
- boolean success = true;
- String message = "";
- File f = IoUtil.getTokenedFile(token);
- try {
- if (f.length() != range.getFrom()) {
- /** drop this uploaded data */
- throw new StreamException(StreamException.ERROR_FILE_RANGE_START);
- }
- out = new FileOutputStream(f, true);
- content = req.getInputStream();
- int read = 0;
- final byte[] bytes = new byte[BUFFER_LENGTH];
- while ((read = content.read(bytes)) != -1)
- out.write(bytes, 0, read);
- start = f.length();
- }catch (StreamException se) {
- success = StreamException.ERROR_FILE_RANGE_START == se.getCode();
- message = "Code: " + se.getCode();
- }catch (FileNotFoundException fne) {
- message = "Code: " + StreamException.ERROR_FILE_NOT_EXIST;
- success = false;
- } catch (IOException io) {
- message = "IO Error: " + io.getMessage();
- success = false;
- } finally {
- IoUtil.close(out);
- IoUtil.close(content);
- /** rename the file */
- if (range.getSize() == start) {
- /** fix the `renameTo` bug */
- // File dst = IoUtil.getFile(fileName);
- // dst.delete();
- // TODO: f.renameTo(dst); 重命名在Windows平台下可能会失败,stackoverflow建议使用下面这句
- try {
- // 先删除
- IoUtil.getFile(fileName).delete();
- Files.move(f.toPath(), f.toPath().resolveSibling(fileName));
- System.out.println("TK: `" + token + "`, NE: `" + fileName + "`");
- /** if `STREAM_DELETE_FINISH`, then delete it. */
- if (Configurations.isDeleteFinished()) {
- IoUtil.getFile(fileName).delete();
- }
- } catch (IOException e) {
- success = false;
- message = "Rename file error: " + e.getMessage();
- }
- }
- try {
- if (success) {
- json.put(START_FIELD, start);
- }
- json.put(UploadController.SUCCESS, success);
- json.put(UploadController.MESSAGE, message);
- } catch (JSONException e) {}
- writer.write(json.toString());
- IoUtil.close(writer);
- }
- }
- @Override
- protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- resp.setContentType("application/json;charset=utf-8");
- resp.setHeader("Access-Control-Allow-Headers", "Content-Range,Content-Type");
- resp.setHeader("Access-Control-Allow-Origin", Configurations.getCrossOrigins());
- resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
- }
- @Override
- public void destroy() {
- super.destroy();
- }
- }
IoUtils:
- /**
- * IO--closing, getting file name ... main function method
- */
- public class IoUtil {
- static final Pattern RANGE_PATTERN = Pattern.compile("bytes \\d+-\\d+/\\d+");
- /**
- * According the key, generate a file (if not exist, then create
- * a new file).
- *
- * @param filename
- * @return
- * @throws IOException
- */
- public static File getFile(String filename) throws IOException {
- if (filename == null || filename.isEmpty())
- return null;
- String name = filename.replaceAll("/", Matcher.quoteReplacement(File.separator));
- File f = new File(Configurations.getFileRepository() + File.separator + name);
- if (!f.getParentFile().exists())
- f.getParentFile().mkdirs();
- if (!f.exists())
- f.createNewFile();
- return f;
- }
- /**
- * 清楚旧的文件
- *
- * @param fileName
- * @return
- */
- public static boolean deleteFile(String fileName){
- boolean flag = false;
- File file = new File(new UploadServiceImpl().filePathForNowDay() + File.separator + fileName);
- if(file.exists()){
- return file.delete();
- }
- return flag;
- }
- /**
- * Acquired the file.
- *
- * @param key
- * @return
- * @throws IOException
- */
- public static File getTokenedFile(String key) throws IOException {
- if (key == null || key.isEmpty())
- return null;
- // 文件保存在服务器上的路径
- File f = new File(new UploadServiceImpl().filePathForNowDay().toString()+key);
- if (!f.getParentFile().exists())
- f.getParentFile().mkdirs();
- if (!f.exists())
- f.createNewFile();
- return f;
- }
- public static void storeToken(String key) throws IOException {
- if (key == null || key.isEmpty())
- return;
- File f = new File(Configurations.getFileRepository() + File.separator + key);
- if (!f.getParentFile().exists())
- f.getParentFile().mkdirs();
- if (!f.exists())
- f.createNewFile();
- }
- /**
- * close the IO stream.
- *
- * @param stream
- */
- public static void close(Closeable stream) {
- try {
- if (stream != null)
- stream.close();
- } catch (IOException e) {
- }
- }
- /**
- * 获取Range参数
- *
- * @param req
- * @return
- * @throws IOException
- */
- public static Range parseRange(HttpServletRequest req) throws IOException {
- String range = req.getHeader(StreamServlet.CONTENT_RANGE_HEADER);
- Matcher m = RANGE_PATTERN.matcher(range);
- if (m.find()) {
- range = m.group().replace("bytes ", "");
- String[] rangeSize = range.split("/");
- String[] fromTo = rangeSize[0].split("-");
- long from = Long.parseLong(fromTo[0]);
- long to = Long.parseLong(fromTo[1]);
- long size = Long.parseLong(rangeSize[1]);
- return new Range(from, to, size);
- }
- throw new IOException("Illegal Access!");
- }
- /**
- * From the InputStream, write its data to the given file.
- */
- public static long streaming(InputStream in, String key, String fileName) throws IOException {
- OutputStream out = null;
- File f = getTokenedFile(key);
- try {
- out = new FileOutputStream(f);
- int read = 0;
- final byte[] bytes = new byte[FormDataServlet.BUFFER_LENGTH];
- while ((read = in.read(bytes)) != -1) {
- out.write(bytes, 0, read);
- }
- out.flush();
- } finally {
- close(out);
- }
- /** rename the file * fix the `renameTo` bug */
- File dst = IoUtil.getFile(fileName);
- dst.delete();
- f.renameTo(dst);
- long length = getFile(fileName).length();
- /** if `STREAM_DELETE_FINISH`, then delete it. */
- if (Configurations.isDeleteFinished()) {
- dst.delete();
- }
- return length;
- }
- }
TokenUtil:
- /**
- * Key Util: 1> according file name|size ..., generate a key;
- * 2> the key should be unique.
- */
- public class TokenUtil {
- /**
- * 生成Token, A(hashcode>0)|B + |name的Hash值| +_+size的值
- * @param name
- * @param size
- * @return
- * @throws Exception
- */
- public static String generateToken(String name, String size)
- throws IOException {
- if (name == null || size == null)
- return "";
- int code = name.hashCode();
- try {
- String token = (code > 0 ? "A" : "B") + Math.abs(code) + "_" + size.trim();
- /** TODO: store your token, here just create a file */
- IoUtil.storeToken(token);
- return token;
- } catch (Exception e) {
- throw new IOException(e);
- }
- }
- }
.............................直接看仓库吧.
官方仓库:https://gitee.com/jiangdx/stream
基本上,看完这些可以缩短40%的时间...哈哈
完整的多文件上传实例(java版)的更多相关文章
- PHP中,文件上传实例
PHP中,文件上传一般是通过move_uploaded_file()来实现的. bool move_uploaded_file ( string filename, string destinati ...
- PHP学习笔记--文件目录操作(文件上传实例)
文件操作是每个语言必须有的,不仅仅局限于PHP,这里我们就仅用PHP进行讲解 php的文件高级操作和文件上传实例我放在文章的最后部分.--以后我还会给大家写一个PHP类似于网盘操作的例子 注意:阅读此 ...
- ExtJS:文件上传实例
ExtJS:文件上传实例 var ext_dateFormat = 'Y-m-d H:i:s'; var dateFormat = 'yyyy-MM-dd HH:mm:ss'; var date = ...
- Grails笔记三:完整的文件上传实例
文件上传在web应用中是比较普遍的,相对于使用jsp等技术实现文件上传,Grails的文件上传着实让人喜爱,因为极其简单,让人看一遍就容易轻松记住!不多说,实例如下: 假设已有一个名为uploadFi ...
- Spring MVC4使用Servlet3 MultiPartConfigElement文件上传实例
在这篇文章中,我们将使用Spring MultipartResolver 实现 StandardServletMultipartResolver在Servlet3环境中实现单点和多文件上传功能.Spr ...
- SpringMVC+ajax文件上传实例教程
原文地址:https://blog.csdn.net/weixin_41092717/article/details/81080152 文件上传文件上传是项目开发中最常见的功能.为了能上传文件,必须将 ...
- 求超大文件上传方案( Java )
最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...
- HTML5 FormData实现文件上传实例
表单提交,文件上传是一个常用又十分麻烦的功能,以前要上传文件通常都是借助插件或者flash来实现,噼里啪啦的加载一大堆东西.自从有了HTML5的FormData后,老板再也不用担心我的上传了. For ...
- Jquery Uploadify多文件上传实例
jQuery Uploadify开发使用的语言是java. 详细的相关文档,可以参考官网的doc:http://www.uploadify.com/documentation/ 官网的讲解还是很详细的 ...
随机推荐
- High Performance Networking in Google Chrome
小结: 1. 小文件存储于一个文件中: 在内部,磁盘缓存(disk cache)实现了它自己的一组数据结构, 它们被存储在一个单独的缓存目录里.其中有索引文件(在浏览器启动时加载到内存中),数据文件( ...
- eolinker使用初体验(一)
1.官网 https://www.eolinker.com 2.安装测试增强插件,由于chrome安装的时候有问题,建议移步firefox浏览器,不纠结.. 3.新建一个单例测试
- Springboot-001-解决nested exception is org.apache.ibatis.binding.BindingException: Parameter 'env' not found. Available parameters are [arg1, arg0, param1, param2]
环境:Springboot + Mybatis + MySQL + VUE 场景: 前端发出数据比对请求,在服务后台与数据库交互时,接口提示错误信息如下所示: { "code": ...
- 类的命名空间&组合
类的命名空间◆类中的静态变量可以被类调用也可以被对象调用◆对不可变数据类型来说,类变量最好用类名操作 class Person: money = 0 mother = Person() father ...
- 插入排序(JAVA实现)
算法思想: 插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕 下图演示了对4个元素进行直接插入排序的过程,共需要(a),(b),(c)三次插入. 代码实现: ...
- [LintCode] 77. Longest common subsequences_ Medium tag: Dynamic Programming
Given two strings, find the longest common subsequence (LCS). Example Example 1: Input: "ABCD&q ...
- Google word/sheets 常见的使用:
Google Sheets: 1, sheets 里面的单元格设置自动换行: 选中单元格: --> Format --> Text Wrapping --> Wrap(自动换行)/C ...
- SpringMVC和Struts2的区别及优势
1.SpringMVC和Struts2的区别比较 1.Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方 ...
- Django框架详细介绍---认证系统
在web开发中通常设计网站的登录认证.注册等功能,Django恰好内置了功能完善的用户认证系统 1.auth模块 from django.contrib import auth 模块源码 import ...
- [批处理]使用Log.io监控日志变化
背景 多台服务器安装了不同的开发服务,增加日志监控以随时处理情况 方案 log.io 环境 NodeJs 安装 1.log.io直接无法安装上,使用log.io-ts安装上 npm install - ...