最近项目需要做上传功能,网上搜索了一下各种的上传插件,最后选择了stream插件,stream支持暂停上传、进度展示、批量上传等核心功能,样式也可以自定义,可以集成bootstrap或layui等框架,比较符合预期。在demo中,我也集成了图片缩略图的生成,视频缩略图的截取等功能,有兴趣的可以看一下

Stream插件:http://twinkling.cn/

首先围观一下

是不是感觉不错,至少我认为是可以的,因为项目本身用的是layui,Stream官网的自定义样式例子用的是bootstrap,我也懒得改了,直接又把bootstrap加入到项目中,有兴趣有时间的同学可以用layui的样式改造一下。

首先说,该控件虽然可以选择多文件同时上传,但文件之间的上传还是串行的,即只允许同时上传一个文件,下面贴出核心的代码,完整的例子详情请在demo中查看

前端代码:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>上传插件集成</title><meta name="renderer" content="webkit"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="format-detection" content="telephone=no"><link rel="stylesheet" href="/layui-2.3.0/css/layui.css" media="all" /><link href="/streamUpload/css/stream-v1.css" rel="stylesheet" type="text/css"><link href="/bootstrap-3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="childrenBody" style="width: 95%"><div class="layui-upload" style="margin-left: 15px;margin-top: 15px;width: 100%"><button type="button" class="layui-btn layui-btn-normal margin-left" id="uploadFileList">选择多文件</button><div class="layui-upload-list" style="width: 100%"><div class="container" style="width: 100%;min-width: 550px;"><div class="row clearfix"><div class="col-md-7 column" style="width: 80%;overflow: auto;max-height: 400px;"><table id="data_table" class="layui-table" lay-skin="line" style="width: 100%;min-width: 500px;"><thead><tr><th>文件名</th><th style="width: 40%">进度</th><th style="min-width: 60px">大小</th><th style="min-width: 70px">操作</th></tr></thead><tbody id="bootstrap-stream-container"></tbody></table></div></div></div><div class="container" style="width: 100%"><div class="row clearfix" style="width: 100%"><div class="col-md-7 column" style="width: 80%"><table style="margin-top: 10px;width: 100%" id="stream_total_progress_bar"><tr><th style="min-width: 500px;"><div class="progress"><div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div></div></th></tr><tr><th><span class="stream_total_size"></span><span class="stream_total_percent"></span></th></tr></table></div></div></div><div class="margin-left"><button type="button" class="layui-btn start" id="startUploadFileButton"><i class="layui-icon"></i>开始上传</button><button type="button" class="layui-btn" id="canleUploadFileButton"><i class="layui-icon"></i>取消上传</button></div></div></div>  <script type="text/javascript" src="/layui-2.3.0/layui.js"></script><script type="text/javascript" src="/streamUpload/js/stream-v1.js"></script><script src="/jquery/jquery-3.3.1.min.js"></script><script type="text/javascript" src="/bootstrap-3.3.7/js/bootstrap.min.js"></script><script type="text/javascript">var config = {enabled: true, /** 是否启用文件选择,默认是true */customered: true,multipleFiles: true, /** 是否允许同时选择多个文件,默认是false */   autoRemoveCompleted: false, /** 是否自动移除已经上传完毕的文件,非自定义UI有效(customered:false),默认是false */autoUploading: false, /** 当选择完文件是否自动上传,默认是true */fileFieldName: "FileData", /** 相当于指定<input type="file" name="FileData">,默认是FileData */maxSize: 2147483648, /** 当_t.bStreaming = false 时(也就是Flash上传时),2G就是最大的文件上传大小!所以一般需要 */simLimit: 200, /** 允许同时选择文件上传的个数(包含已经上传过的) *///extFilters: [".ppt",".pptx",".doc",".docx",".xls",".xlsx",".pdf"], /** 默认是全部允许,即 [] */browseFileId : "uploadFileList", /** 文件选择的Dom Id,如果不指定,默认是i_select_files */browseFileBtn : "", /** 选择文件的按钮内容,非自定义UI有效(customered:false) */filesQueueId : "i_stream_files_queue", /** 文件上传进度显示框ID,非自定义UI有效(customered:false) */filesQueueHeight : 450, /** 文件上传进度显示框的高,非自定义UI有效(customered:false),默认450px */messagerId : "i_stream_message_container", /** 消息框的Id,当没有自定义onXXX函数,系统会显示onXXX的部分提示信息,如果没有i_stream_message_container则不显示 */uploadURL : "/uploadFile",tokenURL : "/queryToken",postVarsPerFile:{},onSelect: function(files) {},onMaxSizeExceed: function(file) {$("#i_error_tips > span.text-message").append("文件[name="+file.name+", size="+file.formatSize+"]超过文件大小限制‵"+file.formatLimitSize+"‵,将不会被上传!<br>");},onFileCountExceed : function(selected, limit) {$("#i_error_tips > span.text-message").append("同时最多上传<strong>"+limit+"</strong>个文件,但是已选择<strong>"+selected+"</strong>个<br>");},onExtNameMismatch: function(info) {$("#i_error_tips > span.text-message").append("<strong>"+info.name+"</strong>文件类型不匹配[<strong>"+info.filters.toString() + "</strong>]<br>");},onAddTask: function(file) {var file = '<tr id="' + file.id + '" class="template-upload fade in">' +'<td><span class="preview">'+file.name+'</span></td>' +'<td>' +'    <div> <span class="message-text" style="font-size: 13px"></span></div>' +'    <div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">' +'            <div class="progress-bar progress-bar-success" title="" style="width: 0%;"></div>' +'       </div>' +'</td>' +'<td><p class="size">' + file.formatSize + '</p>' +'</td>' +'<td><span class="glyphicon glyphicon-remove" onClick="javascript:_t.cancelOne(\'' + file.id + '\')"></span>' +'</td></tr>';$("#bootstrap-stream-container").append(file);},onUploadProgress: function(file) {var $bar = $("#"+file.id).find("div.progress-bar");$bar.css("width", file.percent + "%");var $message = $("#"+file.id).find("span.message-text");$message.text("已上传:" + file.formatLoaded + "/" + file.formatSize + "(" + file.percent + "%" + ") 速  度:" + file.formatSpeed);var $total = $("#stream_total_progress_bar");$total.find("div.progress-bar").css("width", file.totalPercent + "%");$total.find("span.stream_total_size").html(file.formatTotalLoaded + "/" + file.formatTotalSize);$total.find("span.stream_total_percent").html(file.totalPercent + "%");},onStop: function() {},onCancel: function(file) {$("#"+file.id).remove();var $total = $("#stream_total_progress_bar");$total.find("div.progress-bar").css("width", file.totalPercent + "%");$total.find("span.stream_total_size").text(file.formatTotalLoaded + "/" + file.formatTotalSize);$total.find("span.stream_total_percent").text(file.totalPercent + "%");//console && console.log("-------------onCancel-------------------End");},onCancelAll: function(numbers) {$("#i_error_tips > span.text-message").append(numbers + " 个文件已被取消上传!!!");},onComplete: function(file) {/** 100% percent */var $bar = $("#"+file.id).find("div.progress-bar");$bar.css("width", file.percent + "%");var $message = $("#"+file.id).find("span.message-text");$message.text("已上传:" + file.formatLoaded + "/" + file.formatSize + "(" + file.percent + "%" + ")");/** remove the `cancel` button */var $cancelBtn = $("#"+file.id).find("td:last > span");$cancelBtn.remove();/** modify the total progress bar */var $total = $("#stream_total_progress_bar");$total.find("div.progress-bar").css("width", file.totalPercent + "%");$total.find("span.stream_total_size").text(file.formatTotalLoaded + "/" + file.formatTotalSize);$total.find("span.stream_total_percent").text(file.totalPercent + "%");//console && console.log("-------------onComplete-------------------End");},onQueueComplete: function(msg) {$("#startUploadFileButton").html('<i class="layui-icon"></i>开始上传');$("#startUploadFileButton").removeClass("stop").addClass("start");$("#returnListPageButton").removeClass("layui-btn-disabled");$("#returnListPageButton").prop("disabled",false);},onUploadError: function(status, msg) {$("#i_error_tips > span.text-message").append(msg + ", 状态码:" + status);}};var _t = new Stream(config);function cancelOne(id){_t.cancelOne(id);}$("#startUploadFileButton").click(function(){if($(this).hasClass("start")){$(this).html('<i class="layui-icon"></i>暂停上传');$(this).removeClass("start").addClass("stop");$("#returnListPageButton").addClass("layui-btn-disabled");$("#returnListPageButton").prop("disabled",true);_t.upload();}else{$(this).html('<i class="layui-icon"></i>开始上传');$(this).removeClass("stop").addClass("start");$("#returnListPageButton").removeClass("layui-btn-disabled");$("#returnListPageButton").prop("disabled",false);_t.stop();}});$("#canleUploadFileButton").click(function(){_t.cancel();});</script>
</body>
</html>

java代码:

TokenController

@Controller
public class TokenController {public static final String FILE_NAME_FIELD = "name";public static final String FILE_SIZE_FIELD = "size";public static final String FILE_TYPE = "fileType";public static final String TOKEN_FIELD = "token";public static final String SERVER_FIELD = "server";public static final String SUCCESS = "success";public static final String MESSAGE = "message";@RequestMapping(value = "/queryToken")public void firstdoget(HttpServletRequest req, HttpServletResponse resp) throws IOException {doOptions(req, resp);String name = req.getParameter(FILE_NAME_FIELD); // 文件名String size = req.getParameter(FILE_SIZE_FIELD); // 文件大小String token = TokenUtil.generateToken(name, size); // 利用文件名和文件大小重新生成编码作为临时文件的名字PrintWriter writer = resp.getWriter();JSONObject json = new JSONObject();try {json.put(TOKEN_FIELD, token);json.put(SUCCESS, true);json.put(MESSAGE, "");} catch (JSONException e) {}writer.write(json.toString());}protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws UnsupportedEncodingException {req.setCharacterEncoding("UTF-8");resp.setCharacterEncoding("UTF-8");resp.setContentType("application/json;charset=utf-8");resp.setHeader("Access-Control-Allow-Origin", "*");resp.setHeader("Access-Control-Allow-Headers", "Content-Range,Content-Type");resp.setHeader("Access-Control-Allow-Origin", "*");resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");}
}

UploadController

@Controller
public class UploadController {static final int BUFFER_LENGTH = 10240;static final String START_FIELD = "start";public static final String CONTENT_RANGE_HEADER = "content-range";@Autowiredprivate ImageAsync imageAsync;@Autowiredprivate VideoAsync videoAsync;@RequestMapping(value = "/uploadFile", method = RequestMethod.GET)public void uploadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {long start = 0;boolean success = true;String message = "";JSONObject json = new JSONObject();final String token = request.getParameter(TokenController.TOKEN_FIELD); // 文件名和大小的hashcode编码final String size = request.getParameter(TokenController.FILE_SIZE_FIELD); // 文件大小final String fileName = request.getParameter(TokenController.FILE_NAME_FIELD); // 文件名final PrintWriter writer = response.getWriter();try {doOptions(request, response);// 创建空文件,以及上级文件夹名File file = IoUtil.getTokenedFile(token);start = file.length();if (token.endsWith("_0") && "0".equals(size) && 0 == start)file.renameTo(IoUtil.getFile(fileName));} catch (FileNotFoundException e) {message = "Error: " + e.getMessage();success = false;} finally {try {if (success)json.put(START_FIELD, start);json.put(TokenController.SUCCESS, success);json.put(TokenController.MESSAGE, message);} catch (JSONException e) {}writer.write(json.toString());IoUtil.close(writer);}}@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)public void uploadFileListPost(HttpServletRequest request, HttpServletResponse response)throws IOException, Exception {doOptions(request, response);// 文件名和大小的hashcode编码final String token = request.getParameter(TokenController.TOKEN_FIELD);// 文件名final String fileName = request.getParameter(TokenController.FILE_NAME_FIELD);Range range = IoUtil.parseRange(request);OutputStream out = null;InputStream content = null;final PrintWriter writer = response.getWriter();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 = request.getInputStream();int read = 0;final byte[] bytes = new byte[BUFFER_LENGTH];while ((read = content.read(bytes)) != -1) {out.write(bytes, 0, read);try {// 这里主要是控制读写速度,呈现给前端的是上传速度Thread.sleep(ConstantByProperties.uploadSpeed);} catch (InterruptedException e) {e.printStackTrace();}}start = f.length();} catch (StreamException se) {success = StreamException.ERROR_FILE_RANGE_START == se.getCode();message = "Code: " + se.getCode();throw new StreamException(se.getCode());} catch (FileNotFoundException fne) {message = "Code: " + StreamException.ERROR_FILE_NOT_EXIST;success = false;throw new FileNotFoundException();} catch (IOException io) {message = "IO Error: " + io.getMessage();success = false;throw new IOException(io);} finally {IoUtil.close(out);IoUtil.close(content);/** rename the file */Map<String, String> map = new HashMap<String, String>();if (range.getSize() == start) {/** fix the `renameTo` bug */try {String cgSubDir = ConstantByProperties.basePath;String uuid = UUID.randomUUID().toString();String fileSaveName = uuid + "." + fileName.split("\\.")[fileName.split("\\.").length - 1];String url = cgSubDir + fileSaveName;Path pathtrue = f.toPath().resolveSibling(url);File parentFile = pathtrue.toFile().getParentFile();if (!parentFile.exists()) {parentFile.mkdirs();}Files.move(f.toPath(), pathtrue);map.put("name", fileName);map.put("uuid", uuid);map.put("url", url);// 获得上传后的文件对象,可以做一些后期的处理,比如生成图片缩略图,视频缩略图等等,最好用异步的方式去执行这些操作,开启异步配置自行百度File newFile = pathtrue.toFile();// 生成图片缩略图,生成缩略图存放的路径跟原图路径是相同的imageAsync.createThumbnail(newFile);// 异步生成视频缩略图,第二个参数表示取(视频长度/xx)的那一帧作为缩略图videoAsync.randomGrabberFFmpegImage(pathtrue.toString(), 2);} catch (IOException e) {e.printStackTrace();success = false;message = "Rename file error: " + e.getMessage();}}try {if (success) {json.put(START_FIELD, start);json.put("map", map);}json.put(TokenController.SUCCESS, success);json.put(TokenController.MESSAGE, message);} catch (JSONException e) {System.out.println(e.toString());}writer.write(json.toString());IoUtil.close(writer);}}private void doOptions(HttpServletRequest req, HttpServletResponse resp) throws UnsupportedEncodingException {req.setCharacterEncoding("UTF-8");resp.setCharacterEncoding("UTF-8");resp.setContentType("application/json;charset=utf-8");resp.setHeader("Access-Control-Allow-Origin", "*");resp.setHeader("Access-Control-Allow-Headers", "Content-Range,Content-Type");resp.setHeader("Access-Control-Allow-Origin", "*");resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");}
}

demo地址:https://github.com/iceSnowChen/upload-demo

ps:由于公司有加密机制,demo中的java文件会被加密,我做了一下备份,自己改一下就行了

springboot集成Stream上传插件+图片缩略图+视频缩略图相关推荐

  1. stream 上传插件 java_stream: 流式(包含断点续传)上传文件,包括前端和java后台...

    #Stream 上传插件 Stream 是解决不同浏览器上传文件的插件,是Uploadify的Flash版和Html5版的结合! #Stream 简介 Stream 是根据某网的文件上传插件加工而来, ...

  2. 【SpringBoot学习】5、SpringBoot 实现文件上传,图片上传并显示功能

    SpringBoot 实现文件上传,图片上传并显示功能 我先看一下<颈椎病康复指南>再给大家说怎么实现的这两个功能,毕竟只是一个新手,解决这种复杂点的问题(相对而言),还是需要花费大量时间 ...

  3. WordPress彻底禁用上传媒体图片自动生成缩略图及多尺寸图片(亲测可用)

    WordPress默认上传图片的时候会自动生成缩略图及多尺寸的图片文件,大部分网站都用不到这些多余的图片,不仅仅占用空间,而且上传的时候还会消耗额外的性能. 下面仅需两段函数代码即可彻底禁用该功能. ...

  4. SpringBoot 利用MultipartFile上传本地图片生成图片链接

    方法一 实现类: public String fileUpload(MultipartFile file) {if(file == null){return null;}String fileName ...

  5. 批量上传插件(flash,html5,jquery)

    1.jQuery File Upload 官网:http://blueimp.github.com/jQuery-File-Upload/ 在线示例:http://blueimp.github.com ...

  6. 对于阿里云的oss上传本地图片的相关注意点

    #先在阿里云购买储存空间 我的结果如下: 然后下载相关的SDK,下面是C++相关SDK包: https://help.aliyun.com/document_detail/106216.html?sp ...

  7. js插件---IUpload文件上传插件(包括图片)

    js插件---IUpload文件上传插件(包括图片) 一.总结 一句话总结:上传插件找到真正上传位置的代码,这样就可以知道整个上传插件的逻辑了, 找资料还是github+官方 1.如何在js中找到真正 ...

  8. WebUploader文件图片上传插件的使用

    最近在项目中用到了百度的文件图片上传插件WebUploader.分享给大家 需要在http://fex.baidu.com/webuploader/download.html点击打开链接下载WebUp ...

  9. Bootstrap FileInput 图片上传插件

    最近找了一个比较好用的图片和文件上传插件,Bootstrap-file input插件功能如此强大,样式非常美观,并且支持上传文件预览,ajax同步或异步上传,无论是aspx.net 还是MVC.ne ...

  10. uniapp 图片上传插件使用说明

    插件地址:https://ext.dcloud.net.cn/plugin?id=4589 使用说明 本插件是一个云端上传插件,能够将本地的文件包括图片上传到云存储,ImageX是火山引擎推出的专业图 ...

最新文章

  1. 全检体系结构风格浅谈
  2. Numpy中的random模块中的seed方法的作用
  3. mysql的count(*)的优化,获取千万级数据表的总行数
  4. OpenCV2马拉松第22圈——Hough变换直线检測原理与实现
  5. Linux虚拟内存和进程虚拟地址空间简述
  6. 世界上十大数据中心,全球数字经济顶梁柱
  7. C语言课后习题(36)
  8. java 定位打印_Java定位打印(Java location printing).doc
  9. excel制作一个信息录入系统_Excel数据总是重复录入?使用这招,让系统帮你做检查,非常实用...
  10. 五大流程成就网络运维管理的基础
  11. cmd简单代码雨实现方法
  12. zen3 服务器芯片,AMD EPYC霄龙服务器处理器亮相,Zen3架构性能飙升
  13. C++连接MySQL数据库教程|如何连接数据库
  14. linux双机热备软件Rose,Linux Rose HA 双机热备软件原理
  15. android studio trace,天猫Android性能优化1—AndroidStudio内置的Traceview视图
  16. grep 多条件并行满足_grep多个条件
  17. centos7 firefox 安装 java plugin方法
  18. mdf数据库文件打开
  19. 再谈计算机编程的学习
  20. 通过465端口发送邮件

热门文章

  1. Java中Object转化为int类型
  2. mPaaS 月度小报|魔方卡片(Cube)公测,十个卡片模板任意使用
  3. python暴力破解六位密码(数字和大小写字母)
  4. app抓包工具_安卓APP逆向入门分析——破解某APP登陆请求参数
  5. php 微信下载临时素材,php 微信开发之新增上传/获取下载临时素材
  6. CSS3颜色渐变整理
  7. 数据库实验报告一-创建数据库和表
  8. 移植sqlite3到arm-linux上(AM3352)
  9. 2020浙江大学软件学院软件工程考研经验分享
  10. 小程序中的flex_在Flex应用程序中启用辅助功能