编写下载服务器。 第四部分:有效地实现HEAD操作

HEAD是一个经常被遗忘的HTTP方法(动词),其行为类似于GET,但不会返回正文。 您使用HEAD来检查资源的存在(如果不存在,它应该返回404),并确保您的缓存中没有陈旧的版本。 在这种情况下,您期望304 Not Modified ,而200表示服务器具有最新版本。 例如,您可以使用HEAD有效地实施软件更新。 在这种情况下, ETag是您的应用程序版本(生成,标记,提交哈希),并且您具有固定的/most_recent端点。 您的软件会在ETag发送具有当前版本的HEAD请求。 如果没有更新,服务器将以304答复。如果为200,则可以询问用户是否要升级而无需下载软件。 最终请求GET /most_recent将始终下载您软件的最新版本。 HTTP的力量!

在servlet中,默认情况下, HEAD是在doHead() ,您应该重写它。 默认实现只是将委派给GET但会丢弃主体。 这效率不高,尤其是当您从外部(例如Amazon S3)加载资源时。 幸运的是(?)Spring MVC默认情况下不实现HEAD,因此您必须手动执行。 让我们从HEAD的一些集成测试开始:

def 'should return 200 OK on HEAD request, but without body'() {expect:mockMvc.perform(head('/download/' + FileExamples.TXT_FILE_UUID)).andExpect(status().isOk()).andExpect(content().bytes(new byte[0]))
}def 'should return 304 on HEAD request if we have cached version'() {expect:mockMvc.perform(head('/download/' + FileExamples.TXT_FILE_UUID).header(IF_NONE_MATCH, FileExamples.TXT_FILE.getEtag())).andExpect(status().isNotModified()).andExpect(header().string(ETAG, FileExamples.TXT_FILE.getEtag()))
}def 'should return Content-length header'() {expect:mockMvc.perform(head('/download/' + FileExamples.TXT_FILE_UUID)).andExpect(status().isOk()).andExpect(header().longValue(CONTENT_LENGTH, FileExamples.TXT_FILE.size))
}

实际的实现非常简单,但是需要一些重构以避免重复。 下载端点现在接受GET和HEAD:

@RequestMapping(method = {GET, HEAD}, value = "/{uuid}")
public ResponseEntity<Resource> download(HttpMethod method,@PathVariable UUID uuid,@RequestHeader(IF_NONE_MATCH) Optional<String> requestEtagOpt,@RequestHeader(IF_MODIFIED_SINCE) Optional<Date> ifModifiedSinceOpt) {return storage.findFile(uuid).map(pointer -> new ExistingFile(method, pointer)).map(file -> file.handle(requestEtagOpt, ifModifiedSinceOpt)).orElseGet(() -> new ResponseEntity<>(NOT_FOUND));
}

我创建了一个新的抽象ExistingFile ,它封装了我们在其上调用的找到的FilePointer和HTTP动词。 ExistingFile.handle()具有通过HEAD提供文件或仅提供元数据所需的一切:

public class ExistingFile {private static final Logger log = LoggerFactory.getLogger(ExistingFile.class);private final HttpMethod method;private final FilePointer filePointer;public ExistingFile(HttpMethod method, FilePointer filePointer) {this.method = method;this.filePointer = filePointer;}public ResponseEntity<Resource> handle(Optional<String> requestEtagOpt, Optional<Date> ifModifiedSinceOpt) {if (requestEtagOpt.isPresent()) {final String requestEtag = requestEtagOpt.get();if (filePointer.matchesEtag(requestEtag)) {return notModified(filePointer);}}if (ifModifiedSinceOpt.isPresent()) {final Instant isModifiedSince = ifModifiedSinceOpt.get().toInstant();if (filePointer.modifiedAfter(isModifiedSince)) {return notModified(filePointer);}}return serveDownload(filePointer);}private ResponseEntity<Resource> serveDownload(FilePointer filePointer) {log.debug("Serving {} '{}'", method, filePointer);final InputStreamResource resource = resourceToReturn(filePointer);return response(filePointer, OK, resource);}private InputStreamResource resourceToReturn(FilePointer filePointer) {if (method == HttpMethod.GET)return buildResource(filePointer);elsereturn null;}private InputStreamResource buildResource(FilePointer filePointer) {final InputStream inputStream = filePointer.open();return new InputStreamResource(inputStream);}private ResponseEntity<Resource> notModified(FilePointer filePointer) {log.trace("Cached on client side {}, returning 304", filePointer);return response(filePointer, NOT_MODIFIED, null);}private ResponseEntity<Resource> response(FilePointer filePointer, HttpStatus status, Resource body) {return ResponseEntity.status(status).eTag(filePointer.getEtag()).lastModified(filePointer.getLastModified().toEpochMilli()).body(body);}}

resourceToReturn()至关重要。 如果返回null ,Spring MVC将不包含任何主体作为响应。 其他所有内容均保持不变(响应标头等)

编写下载服务器

  • 第一部分:始终流式传输,永远不要完全保留在内存中
  • 第二部分:标头:Last-Modified,ETag和If-None-Match
  • 第三部分:标头:内容长度和范围
  • 第四部分:有效地实现HEAD操作
  • 第五部分:油门下载速度
  • 第六部分:描述您发送的内容(内容类型等)
  • 这些文章中开发的示例应用程序可在GitHub上找到。

翻译自: https://www.javacodegeeks.com/2015/07/writing-a-download-server-part-iv-implement-head-operation-efficiently.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/359423.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【grunt整合版】30分钟学会使用grunt打包前端代码

http://www.itnose.net/detail/6009394.html转载于:https://www.cnblogs.com/zifeiyu/p/4106585.html

十三水算法php_基于PHP+Redis令牌桶限流

一 、场景描述在开发接口服务器的过程中&#xff0c;为了防止客户端对于接口的滥用&#xff0c;保护服务器的资源&#xff0c; 通常来说我们会对于服务器上的各种接口进行调用次数的限制。比如对于某个 用户&#xff0c;他在一个时间段&#xff08;interval&#xff09;内&…

ECSHOP如何增加红包序列号字符

ECSHOP系统线下发放红包时系统生成的红包序列号是在10000的基础上增加四位随机数字。如果当我们要发放大额度红包的时候&#xff0c;这样的序列号规 则难免给人不安全的感觉&#xff0c;万一有无聊的人&#xff0c;蒙几个红包序列号出来&#xff0c;那就亏大了&#xff0c;因为…

Java REST JAX-RS 2.0 –如何处理日期,时间和时间戳记数据类型

无论是X-Form-Urlencoded还是JSON HTTP发布到REST资源端点&#xff0c;对于与日期或时间相关的数据都没有特定的“数据类型”。 大多数开发人员会将这些数据发布为“字符串”&#xff0c;或者只是将它们转换为Unix时间戳值&#xff08;例如1435061152&#xff09;。 但是&#…

html中给div设置的属性怎么样才能拿得到_HTML与CSS结合的三种方式:优先级比较...

所谓实践出真知&#xff0c;只有自己动手去做了&#xff0c;才能得到正确的结论。首先我们看看三种结合方式&#xff1a;通过link标签引入外部css文件通过style标签通过style属性很长一段时间我受这个一段话影响&#xff1a;在html文件中&#xff0c;代码的执行顺序是从外到内&…

Quartz作业调度框架及时间表达式的含义和语法

Quartz 是一个开源的作业调度框架&#xff0c;它完全由 Java 写成&#xff0c;并设计用于 J2SE 和 J2EE 应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。本系统结合通过 Spring 来集成 Quartz 。 Quartz 下载地址 &#x…

spring mvc mysql 实例_Spring+Mybatis+SpringMVC+Maven+MySql搭建实例

一、准备工作1. 首先创建一个表&#xff1a;CREATE TABLE t_user (USER_ID int(11) NOT NULL AUTO_INCREMENT,USER_NAME char(30) NOT NULL,USER_PASSWORD char(10) NOT NULL,USER_EMAIL char(30) NOT NULL,PRIMARY KEY (USER_ID),KEY IDX_NAME (USER_NAME)) ENGINEInnoDB AUTO…

LinkedHashMap 根据PUT顺序排序Map

最近工程里面报表需要合计 &#xff0c; 因为所有的项都是动态的&#xff0c;所以只能动态添加。 思路是使用Map&#xff0c;初始化所有Map&#xff0c;然后在Map中合计并且覆盖。 使用HashMap , 初始化后所有动态项的顺序都乱了。 Map<String , Double> totalMap new …

mysql 表设计 date_mysql 表 Date类型

举个例子给你吧&#xff0c;mysql的 datetime字段有表&#xff1a;CREATE TABLE u_user (id bigint(20) NOT NULL AUTO_INCREMENT,nickname varchar(32) DEFAULT NULL,addtime datetime DEFAULT NULL,PRIMARY KEY (id)) ENGINEInnoDB DEFAULT CHARSETutf8mb4;插入的话INSERT IN…

Oracle学习笔记(三)----------执行计划

查看Oracle执行计划的几种方法 一、通过PL/SQL Dev工具 1、直接File->New->Explain Plan Window&#xff0c;在窗口中执行sql可以查看计划结果。其中&#xff0c;Cost表示cpu的消耗&#xff0c;单位为n%&#xff0c;Cardinality表示执行的行数&#xff0c;等价Rows。 2、…

daocloud创建mysql_GitHub - DaoCloud/php-apache-mysql-sample

如何开发一个 PHP MySQL 的 Docker 化应用目标&#xff1a;基于典型的 LAMP 技术栈&#xff0c;用 Docker 镜像的方式搭建一个 Linux Apache MySQL PHP 的应用 。创建 PHP 应用容器因所有官方镜像均位于境外服务器&#xff0c;为了确保所有示例能正常运行&#xff0c;DaoCl…

HDU2795 Billboard

题目&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid2795 1 #include<cstdio>2 #include<algorithm>3 using namespace std;4 #define lson l,m,rt<<15 #define rson m1,r,rt<<1|16 const int maxn 222222;7 int MAX[maxn << 2];8 int…

自动计算尺寸列表功能案例ios源码

源码HTKDynamicResizingCell&#xff0c;HTKDynamicResizingCell提供自动计算尺寸的TableViewCell/CollectionViewCel&#xff0c;只要设置了合适AutoLayout的约束。<ignore_js_op> 使用方法&#xff1a;使用CocoaPods添加&#xff1a; pod HTKDynamicResizingCell, ~>…

mysql存储过程返回多个值_数据库mysql存储过程之返回多个值的方法示例

本文实例讲述了mysql存储过程之返回多个值的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;mysql存储函数只返回一个值。要开发返回多个值的存储过程&#xff0c;需要使用带有INOUT或OUT参数的存储过程。咱们先来看一个orders表它的结构&#xff1a;mysql> de…

休眠NONSTRICT_READ_WRITE CacheConcurrencyStrategy如何工作

介绍 在我以前的文章中 &#xff0c;我介绍了READ_ONLY CacheConcurrencyStrategy &#xff0c;这是不可变实体图的显而易见的选择。 当缓存的数据可变时&#xff0c;我们需要使用读写缓存策略&#xff0c;本文将介绍NONSTRICT_READ_WRITE二级缓存的工作方式。 内部运作 提交H…

mysql用大白话解释_Java基础--2021Java面试题系列教程--大白话解读

前言序言再高大上的框架&#xff0c;也需要扎实的基础才能玩转&#xff0c;高频面试问题更是基础中的高频实战要点。适合阅读人群Java 学习者和爱好者&#xff0c;有一定工作经验的技术人&#xff0c;准面试官等。阅读建议本教程是系列教程&#xff0c;包含 Java 基础&#xff…

Android开源之行之走进zxing,轻松实现二维码扫描(二)

对于Zxing开源项目的简化上文已给出&#xff0c;源码经过测试且不断修改。众所周知&#xff0c;Zxing项目的扫描是横向的&#xff0c;这么引用的用户体验确实不好&#xff1b;然而盲目的修改会出现拉伸以及样本采集的偏离。所以这里说一下如何将横屏修改为竖屏扫描 解决办法引用…

mdb批量导入mysql_快速将 access 用的 mdb 文件导入到 mysql 里

下载链接&#xff1a;http://www.bullzip.com/download/a2m/msa2mys_5_4_0_274.zipAccess To MySQLAccess to MySQL is a small program that will convert Microsoft Access Databases to MySQL.Wizard interface.Transfer data directly from one server to another.Create a…

动画

1.UIVIew 1.1 动画块(改变背景颜色和移动) static BOOL flagYES; //开始动画 [UIView beginAnimations:nil context:nil]; //运动的时间 [UIView setAnimationDuration:2.f]; //延时启动 [UIView setAnimationDelay:2.f]; //速度曲线 [UIView setAnimationCurve:UIViewAnima…

app engine_Google App Engine:在您自己的域中托管应用程序

app engine在Google App Engine中创建新应用程序时&#xff0c;您将获得一个域名“ yourapp.appspot.com”。 但是&#xff0c;谁会想要以这样的后缀托管他们的应用程序&#xff08;除非您喜欢它&#xff01;&#xff09;&#xff1f; 为了改善您的应用程序品牌&#xff0c;最好…