- 浏览: 3569110 次
- 性别:
- 来自: 大连
-
博客专栏
-
-
使用Titanium Mo...
浏览量:38588
-
-
Cordova 3.x入门...
浏览量:609008
-
-
常用Java开源Libra...
浏览量:684406
-
-
搭建 CentOS 6 服...
浏览量:90534
-
-
Spring Boot 入...
浏览量:403177
-
-
基于Spring Secu...
浏览量:69971
-
-
MQTT入门
浏览量:92333
文章分类
最新评论
-
afateg:
阿里云的图是怎么画出来的?用什么工具?
各云服务平台的架构图 -
cbn_1992:
博主,采用jdbctoken也就是数据库形式之后,反复点击获取 ...
Spring Security OAuth2 Provider 之 数据库存储 -
ipodao:
写的很是清楚了,我找到一份中文协议:https://mcxia ...
MQTT入门(6)- 主题Topics -
Cavani_cc:
还行
MQTT入门(6)- 主题Topics -
fexiong:
博主,能否提供完整源码用于学习?邮箱:2199611997@q ...
TensorFlow 之 构建人物识别系统
根据 HTTP/1.1 协议,客户端可以获取 response 资源的一部分,以便由于在通信中断后还能够继续前一次的请求,常见的场景包括大文件下载、视频播放的前进/后退等。
以下是一个Byte-Range请求的具体HTTP信息:
详细说明可以参考HTTP/1.1(RFC 2616)的以下部分:
(0)Client Request -> (1)Web Server -> (2)Servlet Container -> (3)Web Framework -> (4) Your Code
可以通过 Spring MVC 的 XmlViewResolver中注册一个自定义的View,在Controller中返回该View来实现。
这样具体的Byte-Range请求处理都将会在ByteRangeViewRender中进行。ByteRangeViewRender的具体实现可以参考Tomcat的DefaultServlet.java和Spring的ResourceHttpRequestHandler.java。
以下是一个写好的View:
通过ProgressListener实现大文件上传时进度条的显示
1)application-context.xml
2)CustomMultipartResolver.java
3)CustomProgressListener.java
4)ProgressInfo.java
5)Controller
6)JSP
参考:
How to Implement HTTP byte-range requests in Spring MVC
Implementing HTTP byte-range requests in Spring MVC
FileServlet supporting resume and caching and GZIP
以下是一个Byte-Range请求的具体HTTP信息:
引用
【Status Code】
206 Partial Content (出错时416)
【Request Headers】
Range:bytes=19448183-
【Response Headers】
Accept-Ranges:bytes
Content-Length:58
Content-Range:bytes 19448183-19448240/19448241
Content-Type:video/mp4
206 Partial Content (出错时416)
【Request Headers】
Range:bytes=19448183-
【Response Headers】
Accept-Ranges:bytes
Content-Length:58
Content-Range:bytes 19448183-19448240/19448241
Content-Type:video/mp4
详细说明可以参考HTTP/1.1(RFC 2616)的以下部分:
- 3.12 Range Units
- 14.5 Accept-Ranges
- 14.16 Content-Range
- 14.27 If-Range
- 14.35 Range
(0)Client Request -> (1)Web Server -> (2)Servlet Container -> (3)Web Framework -> (4) Your Code
- Web Server: 一般遵循HTTP/1.1 协议都支持Byte-Range请求,比如Apache、Ngnix
- Servlet Container:大部分也支持,比如Tomcat的DefaultServlet.java
- Web Framework: Spring4.2开始支持,具体可以查看ResourceHttpRequestHandler.java
可以通过 Spring MVC 的 XmlViewResolver中注册一个自定义的View,在Controller中返回该View来实现。
ModelAndView mv = new ModelAndView("byteRangeViewRender"); mv.addObject("file", new File("C:\\RenSanNing\\xxx.mp4")); mv.addObject("contentType", "video/mp4"); return mv;
这样具体的Byte-Range请求处理都将会在ByteRangeViewRender中进行。ByteRangeViewRender的具体实现可以参考Tomcat的DefaultServlet.java和Spring的ResourceHttpRequestHandler.java。
以下是一个写好的View:
public class ByteRangeViewRender extends AbstractView { // Constants ---------------------------------------------------------------------------------- private static final int DEFAULT_BUFFER_SIZE = 20480; // ..bytes = 20KB. private static final long DEFAULT_EXPIRE_TIME = 604800000L; // ..ms = 1 week. private static final String MULTIPART_BOUNDARY = "MULTIPART_BYTERANGES"; @Override protected void renderMergedOutputModel(Map<String, Object> objectMap, HttpServletRequest request, HttpServletResponse response) throws Exception { File file = (File) objectMap.get("file"); if (file == null || !file.exists()) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } String contentType = (String) objectMap.get("contentType"); String fileName = file.getName(); long length = file.length(); long lastModified = file.lastModified(); String eTag = fileName + "_" + length + "_" + lastModified; long expires = System.currentTimeMillis() + DEFAULT_EXPIRE_TIME; // Validate request headers for caching --------------------------------------------------- // If-None-Match header should contain "*" or ETag. If so, then return 304. String ifNoneMatch = request.getHeader("If-None-Match"); if (ifNoneMatch != null && matches(ifNoneMatch, fileName)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); response.setHeader("ETag", eTag); // Required in 304. response.setDateHeader("Expires", expires); // Postpone cache with 1 week. return; } // If-Modified-Since header should be greater than LastModified. If so, then return 304. // This header is ignored if any If-None-Match header is specified. long ifModifiedSince = request.getDateHeader("If-Modified-Since"); if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); response.setHeader("ETag", eTag); // Required in 304. response.setDateHeader("Expires", expires); // Postpone cache with 1 week. return; } // Validate request headers for resume ---------------------------------------------------- // If-Match header should contain "*" or ETag. If not, then return 412. String ifMatch = request.getHeader("If-Match"); if (ifMatch != null && !matches(ifMatch, fileName)) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return; } // If-Unmodified-Since header should be greater than LastModified. If not, then return 412. long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since"); if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return; } // Validate and process range ------------------------------------------------------------- // Prepare some variables. The full Range represents the complete file. Range full = new Range(0, length - 1, length); List<Range> ranges = new ArrayList<Range>(); // Validate and process Range and If-Range headers. String range = request.getHeader("Range"); if (range != null) { // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416. if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) { response.setHeader("Content-Range", "bytes */" + length); // Required in 416. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } String ifRange = request.getHeader("If-Range"); if (ifRange != null && !ifRange.equals(eTag)) { try { long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid. if (ifRangeTime != -1 && ifRangeTime + 1000 < lastModified) { ranges.add(full); } } catch (IllegalArgumentException ignore) { ranges.add(full); } } // If any valid If-Range header, then process each part of byte range. if (ranges.isEmpty()) { for (String part : range.substring(6).split(",")) { // Assuming a file with length of 100, the following examples returns bytes at: // 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100). long start = sublong(part, 0, part.indexOf("-")); long end = sublong(part, part.indexOf("-") + 1, part.length()); if (start == -1) { start = length - end; end = length - 1; } else if (end == -1 || end > length - 1) { end = length - 1; } // Check if Range is syntactically valid. If not, then return 416. if (start > end) { response.setHeader("Content-Range", "bytes */" + length); // Required in 416. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } // Add range. ranges.add(new Range(start, end, length)); } } } // Prepare and initialize response -------------------------------------------------------- // Get content type by file name and set content disposition. String disposition = "inline"; // If content type is unknown, then set the default value. // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp // To add new content types, add new mime-mapping entry in web.xml. if (contentType == null) { contentType = "application/octet-stream"; } else if (!contentType.startsWith("image")) { // Else, expect for images, determine content disposition. If content type is supported by // the browser, then set to inline, else attachment which will pop a 'save as' dialogue. String accept = request.getHeader("Accept"); disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment"; } // Initialize response. response.reset(); response.setBufferSize(DEFAULT_BUFFER_SIZE); response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\""); response.setHeader("Accept-Ranges", "bytes"); response.setHeader("ETag", eTag); response.setDateHeader("Last-Modified", lastModified); response.setDateHeader("Expires", expires); // Send requested file (part(s)) to client ------------------------------------------------ // Prepare streams. RandomAccessFile input = null; OutputStream output = null; try { // Open streams. input = new RandomAccessFile(file, "r"); output = response.getOutputStream(); if (ranges.isEmpty() || ranges.get(0) == full) { // Return full file. Range r = full; response.setContentType(contentType); response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total); response.setHeader("Content-Length", String.valueOf(r.length)); copy(input, output, r.start, r.length); } else if (ranges.size() == 1) { // Return single part of file. Range r = ranges.get(0); response.setContentType(contentType); response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total); response.setHeader("Content-Length", String.valueOf(r.length)); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Copy single part range. copy(input, output, r.start, r.length); } else { // Return multiple parts of file. response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Cast back to ServletOutputStream to get the easy println methods. ServletOutputStream sos = (ServletOutputStream) output; // Copy multi part range. for (Range r : ranges) { // Add multipart boundary and header fields for every range. sos.println(); sos.println("--" + MULTIPART_BOUNDARY); sos.println("Content-Type: " + contentType); sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total); // Copy single part range of multi part range. copy(input, output, r.start, r.length); } // End with multipart boundary. sos.println(); sos.println("--" + MULTIPART_BOUNDARY + "--"); } } finally { close(output); close(input); } } // Helpers (can be refactored to public utility class) ---------------------------------------- private void close(Closeable resource) { if (resource != null) { try { resource.close(); } catch (IOException ignore) { } } } private void copy(RandomAccessFile input, OutputStream output, long start, long length) throws IOException { byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int read; try { if (input.length() == length) { // Write full range. while ((read = input.read(buffer)) > 0) { output.write(buffer, 0, read); } } else { input.seek(start); long toRead = length; while ((read = input.read(buffer)) > 0) { if ((toRead -= read) > 0) { output.write(buffer, 0, read); } else { output.write(buffer, 0, (int) toRead + read); break; } } } } catch (IOException ignore) { } } private long sublong(String value, int beginIndex, int endIndex) { String substring = value.substring(beginIndex, endIndex); return (substring.length() > 0) ? Long.parseLong(substring) : -1; } private boolean accepts(String acceptHeader, String toAccept) { String[] acceptValues = acceptHeader.split("\\s*(,|;)\\s*"); Arrays.sort(acceptValues); return Arrays.binarySearch(acceptValues, toAccept) > -1 || Arrays.binarySearch(acceptValues, toAccept.replaceAll("/.*$", "/*")) > -1 || Arrays.binarySearch(acceptValues, "*/*") > -1; } private boolean matches(String matchHeader, String toMatch) { String[] matchValues = matchHeader.split("\\s*,\\s*"); Arrays.sort(matchValues); return Arrays.binarySearch(matchValues, toMatch) > -1 || Arrays.binarySearch(matchValues, "*") > -1; } // Inner classes ------------------------------------------------------------------------------ protected class Range { long start; long end; long length; long total; public Range(long start, long end, long total) { this.start = start; this.end = end; this.length = end - start + 1; this.total = total; } } }
通过ProgressListener实现大文件上传时进度条的显示
1)application-context.xml
<bean id="multipartResolver" class="com.rensanning.test.core.fileupload.CustomMultipartResolver"> <property name="defaultEncoding" value="UTF-8"/> <property name="fileSizeMax" value="20971520"/><!-- 20M : Maximum size of a single uploaded file--> <property name="maxUploadSize" value="52428800"/><!-- 50M : The maximum allowed size of a complete request--> </bean>
2)CustomMultipartResolver.java
public class CustomMultipartResolver extends CommonsMultipartResolver { @Autowired private CustomProgressListener progressListener; public void setFileUploadProgressListener(CustomProgressListener progressListener){ this.progressListener = progressListener; } public void setFileSizeMax(long fileSizeMax) { getFileUpload().setFileSizeMax(fileSizeMax); } @Override public MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException { String encoding = determineEncoding(request); FileUpload fileUpload = prepareFileUpload(encoding); progressListener.setSession(request.getSession()); fileUpload.setProgressListener(progressListener); try { List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request); return parseFileItems(fileItems, encoding); } catch (FileUploadBase.SizeLimitExceededException ex) { throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex); } catch (FileUploadBase.FileSizeLimitExceededException ex) { throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex); } catch (FileUploadException ex) { throw new MultipartException("Could not parse multipart servlet request", ex); } } }
3)CustomProgressListener.java
@Component public class CustomProgressListener implements ProgressListener { private HttpSession session; public void setSession(HttpSession session){ this.session = session; ProgressInfo ps = new ProgressInfo(); this.session.setAttribute(Constants.SESSION_KEY_UPLOAD_PROGRESS_INFO, ps); } @Override public void update(long pBytesRead, long pContentLength, int pItems) { ProgressInfo ps = (ProgressInfo) session.getAttribute(Constants.SESSION_KEY_UPLOAD_PROGRESS_INFO); ps.setBytesRead(pBytesRead); ps.setContentLength(pContentLength); ps.setItemSeq(pItems); } }
4)ProgressInfo.java
public class ProgressInfo { private long bytesRead; private long contentLength; private int itemSeq; public long getBytesRead() { return bytesRead; } public void setBytesRead(long bytesRead) { this.bytesRead = bytesRead; } public long getContentLength() { return contentLength; } public void setContentLength(long contentLength) { this.contentLength = contentLength; } public int getItemSeq() { return itemSeq; } public void setItemSeq(int itemSeq) { this.itemSeq = itemSeq; } }
5)Controller
@ResponseBody @RequestMapping(value = "admin/common/getProgress.do", method = RequestMethod.GET) public String getProgress(HttpServletRequest request, HttpServletResponse response) { if (request.getSession().getAttribute(Constants.SESSION_KEY_UPLOAD_PROGRESS_INFO) == null) { return ""; } ProgressInfo ps = (ProgressInfo) request.getSession().getAttribute(Constants.SESSION_KEY_UPLOAD_PROGRESS_INFO); Double percent = 0d; if (ps.getContentLength() != 0L) { percent = (double) ps.getBytesRead() / (double) ps.getContentLength() * 1.0d; if (percent != 0d) { DecimalFormat df = new DecimalFormat("0.00"); percent = Double.parseDouble(df.format(percent)); } } int pp = (int)(percent * 100); return String.valueOf(pp); }
6)JSP
<div class="control-group" id="progressbar" style="display:none;"> <div class="progress progress-striped active"> <div class="bar" id="progressbardata" style="width: 0;"></div> </div> </div> <script language="javascript"> function upload() { $('#uploadForm').submit(); var interval = setInterval(function() { $.ajax({ dataType : "json", url : "<%=request.getContextPath()%>/admin/common/getProgress.do", contentType : "application/json; charset=utf-8", type : "GET", success : function(data, stats) { if(data) { $('#progressbar').show(); console.log(data); if (data == '100') { clearInterval(interval); } else { $('#progressbardata').width(data+'%'); } } } }); }, 100); } </script>
参考:
How to Implement HTTP byte-range requests in Spring MVC
Implementing HTTP byte-range requests in Spring MVC
FileServlet supporting resume and caching and GZIP
- ByteRangeViewRender.zip (3.4 KB)
- 下载次数: 357
评论
2 楼
ausit
2015-12-13
请问,函数在getFileUpload().setFileSizeMax(fileSizeMax);
在哪里 ?能分享个能跑的demo么,谢谢
在哪里 ?能分享个能跑的demo么,谢谢
1 楼
lis1314
2015-10-28
mark!

相关推荐
内容概要:本文介绍了一个用C++实现的综合性的仓库入口安全防御系统,主要包括四个模块:用户认证系统(AuthenticationSystem)、防火墙系统(FirewallSystem)、入侵检测系统(IntrusionDetectionSystem)以及整体管理的仓库安全系统(WarehouseSecuritySystem)。每个模块都承担了各自的重要职责来确保整个系统免受非法访问与潜在风险。具体来说,用户认证部分实现了账户管理及其密码保护策略,并引入了多次连续登陆失败即锁定的功能以防范暴力破解行为。防火墙系统则能够设定规则,筛选出不合法或者可疑的数据流量从而进行阻拦,同时也可以针对指定的目标设备或地址设置限制条件;入侵检测系统记录每一次进出情况并对那些带有预定义危险信号的行为发出警告提示。最后仓库安全管理部分整合了前两者,并负责全面协调各项安全机制的运作。 适合人群:具备C++编程能力的专业安全工程师或者有一定网络攻防理论背景的研发和技术管理人员。 使用场景及目标:本项目特别适用于有高网络安全防护需求的企业和个人服务器管理者,特别是对黑客入侵可能性较高的行业比如金融、政府等领域而言
屏幕PCB LAYOUT
netCDF4-1.6.4-cp311-cp311-win32.whl.zip
turbowarp下载
CH341编程器软件NeoProgrammer
QRCodeScaner.zip
netCDF4-1.6.2-cp38-cp38-win32.whl.zip
上市公司-企业超额商誉数据(2005-2023年)
IATF16949汽车认证方案获得并保持IATF认可的规则 第六版.pdf
Notepad_202503231401_51092.png
智慧能源集团数字化管理平台项目建议书.docx
循环神经网络可应用于处理时间序列的数据。本人提供了一份与股票相关的时间序列数据,包含股票的开盘数据,关盘数据、最高点数据、最低点数据。供大家学习训练时使用
jdk-8u181 windows和linux安装包
内容概要:本文档展示了用C++编写的简单仓库入口安全防护系统的实现案例,详细介绍了系统的组成模块:用户认证系统利用SHA-256加密方式保护用户登录信息的安全;防火墙系统负责拦截来自特定危险区域(如某些指定IP地址范围或非法端口号)的数据流;二者共同协作以确保只有经过授权并且来源可靠的设备才被允许进入仓库区域。此外,文中还提供了完整的代码清单及其运行环境搭建指南,帮助开发者快速理解并实践这套安全解决方案的核心思路。 适合人群:对于有一定C++编程基础的学习者或开发人员来说尤为合适,他们可以借鉴此实例深化对软件安全防护机制的理解,并学会应用这些知识点来构建类似的应用场景。 使用场景及目标:该系统主要用于模拟真实的仓储设施出入口处的身份查验及网络接入管理,能够为学员提供直观的学习范本。它可以帮助使用者掌握如何整合不同的安全元素形成有效的防护措施,同时培养解决问题的能力以及团队合作精神。 其他说明:本实例仅供参考和练习目的,真实环境下建议采用更为专业可靠的产品和服务进行部署。由于采用了OpenSSL提供的哈希函数接口来进行敏感数据的加解密操作,所以项目实施前请务必确认已正确配置好相关依赖库。
笔记记录分享网站,系统包含两种角色:管理员、用户,系统分为前台和后台两大模块,主要功能如下。 前台: - 首页:展示网站的基本信息、热门笔记、公告等内容。 - 用户:展示注册用户的个人信息和活跃度等。 - 公告信息:展示网站发布的最新公告和通知。 - 笔记广场:用户可以在此发布和分享自己的笔记记录。 - 商品:展示网站相关的商品信息。 - 个人中心:用户可以管理个人信息、查看自己的笔记记录等。 - 购物车:用户可以将感兴趣的商品加入购物车。 后台: - 管理员个人中心:管理员可以管理个人信息,包括修改密码、查看个人信息等。 - 管理员管理:管理员可以管理其他管理员账号,包括添加、删除、编辑等操作。 - 基础数据管理:管理员可以管理系统的基础数据,如网站配置、分类信息等。 - 笔记广场管理:管理员可以管理笔记广场的内容,包括审核、删除违规内容等操作。 - 公告信息管理:管理员可以管理网站发布的公告信息,包括添加、删除、编辑等操作。 - 日常任务管理:管理员可以管理网站的日常任务,如定时备份、数据清理等。 - 商品管理:管理员可以管理网站的商品信息,包括上架、下架、编辑商品信息等操作。
基于vue3+js开发的个人网页,用于设计师或者开发者展示作品的响应式网页模板。.zip
te,dl,快递费空间都死了
注塑磨具分拣机20161207.smbp
## 一、引言 Stata是一款常用于社会科学、经济学等领域的统计软件,提供了丰富的实证分析命令。在数据管理方面,常用命令如use(加载数据)、gen(生成变量)、drop(删除变量)等;描述性统计可通过summary、tabulate和histogram等命令来进行;回归分析则依赖于regress(线性回归)、logit和probit(二项选择回归)等;假设检验可以通过ttest(t检验)和anova(方差分析)完成;面板数据分析则使用xtset和xtreg命令;此外,Stata还提供了多种可视化命令,如scatter和twoway等,帮助用户生成各种图表进行结果展示 为了帮助研究人员更好地利用Stata,马克团队整理了一套Stata实证命令汇总,覆盖了从数据的初步处理到高级统计分析的各个环节 ## 二、目录 一、数据导入和管理 (1)数据导入 (2)数据导出 二、数据的处理 (1)生成新变量 (2)格式转换 (3)缺失数据 (4)异常数据 (5)重命名变量 (6)编码分类变量 (7)设定面板数据 (8)数据合并 (9) 数据追加 三、描述性统计 (1)基本统计 (2)变量的详细统计 (3)变量的频率表 (4)变量间的相关性 (5)回归分析及其描述性统计 (6)简单统计 四、相关性分析 (1)绘制直方图 (2)绘制散点图 (3)矩阵散点图 (4)相关图 (5)回归拟合图 (6)相关系数 (7)相关系数矩阵 五、实证模型 (1)单变量分析 (2)OLS回归 (3)分位数回归 (4)Probit模型 (5)Logit模型 (6)Tobit模型 六、内生性解决 (1)工具变量法 (2)固定效应模型 (3)随机效应模型 (4)系统GMM模型 (5)DID模型 (6)PSM模型 (7)滞后期模型 七、检验分析 (1)豪斯曼检验 (2)Heckman两阶段检验 (3)调节效应检验 (4)中介效应检验 八、结果导出 (1)导出描述性统计 (2)导出相关系数 (3)导出回归结果 ## 三、概览