diff --git a/.gitignore b/.gitignore index 8d58268..4b86c52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,31 @@ -*.class - -# Package Files # -*.jar -*.war -*.ear - -# Build Artifacts - -.gradle/ -build/ +HELP.md target/ -bin/ -dependency-reduced-pom.xml - -# Eclipse Project Files +.mvn +### STS ### +.apt_generated .classpath +.factorypath .project -.settings/ - -# IntelliJ IDEA Files +.settings +.springBeans +.sts4-cache +### IntelliJ IDEA ### +.idea +*.iws *.iml *.ipr -*.iws -*.idea -README.html -gradlew -gradlew.bat -gradle \ No newline at end of file +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md index 8334354..60c28f2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,4 @@ # Breakpoint-http - -![wen icon](https://fourwenwen.github.io/fww/image/logo100_100.jpg) - ## 目录 * [背景介绍](#背景介绍) * [项目介绍](#项目介绍) @@ -17,99 +14,81 @@ * [秒传功能](#秒传功能) * [断点续传](#断点续传) * [总结](#总结) +* [后续](#后续) + + ## 背景介绍 +*Breakpoint-http*,是不是觉得这个名字有点low,break point断点。这是一个大文件上传的一种实现。因为本来很久没写过前端了,本来想自己好好写一番js,可惜因为种种原因而作罢了。该项目是基于一款百度开源的前端上传控件:**WebUploader**。 +*Breakpoint-http*,当初想实现这一块web大文件上传,是因为有一天同时询问我这方面的知识,我发现好像在实战中没写过这类的代码啊。既然知道了自己不足那肯定要狠狠补一下。所以才有了这个项目。这个项目是gradle+Spring Boot可能有部分人还没接触过这两个东西,这里就不进行讲解了,毕竟这不是重点,把gradle当成maven吧,虽然它还有更出色的功能。Spring Boot用来简化Spring应用的初始搭建以及开发过程,一个约定大于规范的框架。 -  *Breakpoint-http*,是不是觉得这个名字有点low,break point断点。这是一个大文件上传的一种实现。因为本来很久没写过前端了,本来想自己好好写一番js,可惜 -因为种种原因而作罢了。该项目是基于一款百度开源的前端上传控件:WebUploader(百度开源的东西文档一如既往的差,哈哈。或者是我理解能力差)。
-  *Breakpoint-http*,当初想实现这一块web大文件上传,是因为有一天同时询问我这方面的知识,我发现好像在实战中没写过这类的代码啊。既然知道了自己不足那 -肯定要狠狠补一下。所以才有了这个项目。 - -  对了这个项目是gradle+Spring Boot可能有部分人还没接触过这两个东西,这里就不进行讲解了,毕竟这不是重点,把gradle当成maven吧,虽然它还有更出色的功能。 -Spring Boot用来简化Spring应用的初始搭建以及开发过程,一个约定大于规范的框架。 ## 项目介绍 - -  *Breakpoint-http* 是一个基于大文件上传,并参考网盘上传文件,而基于web的大文件上传实现项目。web中上传大文件没有桌面软件那么容易,还好现在是身处于 -一个html5的时代。我们web端上传文件常用的做法就是用表单上传了,一旦上传的文件大小较大,一旦带宽跟不上,那用户只能在哪里一直等着,不能做刷新页面的操作, -并且一旦产生网络波动,那么用户所做的一切就白费了。 -  *Breakpoint-http*就是为了保证在web端上传大文件能达到基本的可靠性的一种方案,方法多种,可能的方案会更出色,欢迎讨论。要让大文件上传能达到可用性,我们需要做到怎么样的程度呢? +*Breakpoint-http* 是一个基于大文件上传,并参考网盘上传文件,而基于web的大文件上传实现项目。web中上传大文件没有桌面软件那么容易,还好现在是身处于一个html5的时代。我们web端上传文件常用的做法就是用表单上传了,一旦上传的文件大小较大,一旦带宽跟不上,那用户只能在哪里一直等着,不能做刷新页面的操作,并且一旦产生网络波动,那么用户所做的一切就白费了。 +*Breakpoint-http*就是为了保证在web端上传大文件能达到基本的可靠性的一种方案,方法多种,可能的方案会更出色,欢迎讨论。要让大文件上传能达到可用性,我们需要做到怎么样的程度呢? * **断点续传** 最主要的功能之一,在断网或者在暂停的情况下,能够在上传断点中继续上传。 * **分块上传** 也是断点续传的基础之一,把大文件通过前端分块,然后后台在组在一起。 -* **文件妙传** 这个相信大家在网盘中见过不少了,就是服务中已经有人上传过得文件,其他人再上传这个文件就秒上传到服务中去。 -* **其他功能** 把下面这些功能归类到其他,是因为它们基本都是通过WebUploader()来实现的,很简单。 +* **文件秒传** 这个相信大家在网盘中见过不少了,就是服务中已经有人上传过得文件,其他人再上传这个文件就秒上传到服务中去。 +* **其他功能** 把下面这些功能归类到其他,是因为它们基本都是通过[WebUploader]()来实现的,很简单。 * *多线程上传* 多个线程上传不同的块文件。 * *文件进度显示* 显示文件的上传完成情况。 - * UI等等。 - + * *UI* 等等 + + + ## 使用说明 ### 获取代码 -* GitHub: -* OSChina项目主页:
-持续更新。 - +* GitHub: ### 需要知识点 -- 基于spring boot开发的。 +- 基于spring boot开发的,springboot1.x和springboot2.x有较大的差别,需要注意,推荐springboot2.x,其是当前使用的主流框架。 - WebUploader,WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。 -- redis,key-value存储系统,在这里我把redis用作存储文件路径来使用。 -- Gradle,Gradle是一个基于JVM的构建工具。这里我用Gradle顶替了Maven。嗯,多学点东西。 - +- redis, key-value存储系统,在这里我把redis用作存储文件路径来使用。 +- Gradle, Gradle是一个基于JVM的构建工具。这里我用Gradle顶替了Maven。 ### 启动项目 - -1. main方法直接运行: -(1)找到App启动类(win.pangniu.learn包下) -(2)执行main方法。 +1. main方法直接运行: +(1)找到BreakPointHttpApplication启动类(com.zgy.learn包下) +(2)执行main方法。 (3)然后用浏览器访问: - -2. tomcat运行: -(1)执行命令gradle war。 -(2)在out目录下找到bphttp.war包。 -(3)拷贝到tomcat,然后运行tomcat。 -(4)然后用浏览器访问: - ### 项目示范 - 1. 上传完后的页面 -![duan1](https://fourwenwen.github.io/fww/image/bphttp/duan1.jpg) -2. 妙传功能演示页面 -![duan1](https://fourwenwen.github.io/fww/image/bphttp/duan1.jpg) +![](https://raw.githubusercontent.com/prayjourney/img-home/master/img/20210129200147.jpg) +2. 秒传功能演示页面 +![](https://raw.githubusercontent.com/prayjourney/img-home/master/img/20210129200218.jpg) +3. 修改过后的样式 +![](https://raw.githubusercontent.com/prayjourney/img-home/master/img/20210129200623.png) 详情自己运行就知道。 + + ## 核心讲解 ### 核心原理 -  该项目核心就是文件分块上传。前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题。 -  * 如何分片; -  * 如何合成一个文件; -  * 中断了从哪个分片开始。 -  如何分,利用强大的js库,来减轻我们的工作,市场上已经能有关于大文件分块的轮子,虽然程序员的天性曾迫使我重新造轮子。但是因为时间的关系还有工作的关系,我只能罢休了。最后我选择了百度的WebUploader来实现前端所需。 -  如何合,在合之前,我们还得先解决一个问题,我们如何区分分块所属那个文件的。刚开始的时候,我是采用了前端生成了唯一uuid来做文件的标志,在每个分片请求上带上。不过后来在做秒传的时候我放弃了,采用了Md5来维护分块和文件关系。 -  在服务端合并文件,和记录分块的问题,在这方面其实行业已经给了很好的解决方案了。参考迅雷,你会发现,每次下载中的时候,都会有两个文件,一个文件主体,另外一个就是文件临时文件,临时文件存储着每个分块对应字节位的状态。 -这些都是需要前后端密切联系才能做好,前端需要根据固定大小对文件进行分片,并且请求中要带上分片序号和大小。前端发送请求顺利到达后台后,服务器只需要按照请求数据中给的分片序号和每片分块大小(分片大小是固定且一样的)算出开始位置,与读取到的文件片段数据,写入文件即可。 +该项目核心就是文件分块上传。前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题。 +* 如何分片; +* 如何合成一个文件; +* 中断了从哪个分片开始。 +**如何分**,利用强大的js库,来减轻我们的工作,市场上已经能有关于大文件分块的轮子,虽然程序员的天性曾迫使我重新造轮子。但是因为时间的关系还有工作的关系,我只能罢休了。最后我选择了百度的WebUploader来实现前端所需。 +**如何合**,在合之前,我们还得先解决一个问题,我们如何区分分块所属那个文件的。刚开始的时候,我是采用了前端生成了唯一uuid来做文件的标志,在每个分片请求上带上。不过后来在做秒传的时候我放弃了,*采用了Md5来维护分块和文件关系*。 +在服务端合并文件,和记录分块的问题,在这方面其实行业已经给了很好的解决方案了。参考迅雷,你会发现,每次下载中的时候,都会有两个文件,一个文件主体,另外一个就是文件临时文件,临时文件存储着每个分块对应字节位的状态。这些都是需要前后端密切联系才能做好,前端需要根据固定大小对文件进行分片,并且请求中要带上分片序号和大小。前端发送请求顺利到达后台后,服务器只需要按照请求数据中给的分片序号和每片分块大小(分片大小是固定且一样的)算出开始位置,与读取到的文件片段数据,写入文件即可。 + ## 功能分析 - ### 分块上传 -  分块上传可以说是我们整个项目的基础,像断点续传、暂停这些都是需要用到分块。 -分块这块相对来说比较简单。前端是采用了webuploader,分块等基础功能已经封装起来,使用方便。 -借助webUpload提供给我们的文件API,前端就显得异常简单。 - - - -``` - // 实例化wu +分块上传可以说是我们整个项目的基础,像断点续传、暂停这些都是需要用到分块。分块这块相对来说比较简单。前端是采用了webuploader,分块等基础功能已经封装起来,使用方便。借助webuploader提供给我们的文件API,前端就显得异常简单。 +```javascript + // 实例化 var uploader = WebUploader.create({ pick: { id: '#picker', @@ -136,10 +115,10 @@ Spring Boot用来简化Spring应用的初始搭建以及开发过程,一个约 fileSingleSizeLimit: 1024 * 1024 * 1024 // 50 M }); ``` -  分则必合。把大文件分片了,但是分片了就没有原本文件功能,所以我们要把分片合成为原本的文件。我们只需要把分片按原本位置写入到文件中去。因为前面原理那一部我们已经讲到了,我们知道分块大小和分块序号,我就可以知道该分块在文件中的起始位置。所以这里使用RandomAccessFile是明智的,RandomAccessFile能在文件里面前后移动。但是在andomAccessFile的绝大多数功能,已经被JDK1.4的NIO的“内存映射文件(memory-mapped files)”取代了。我在该项目中分别写了使用RandomAccessFile与MappedByteBuffer来合成文件。分别对应的方法是uploadFileRandomAccessFile和uploadFileByMappedByteBuffer。两个方法代码如下。 +**分则必合**。把大文件分片了,但是分片了就没有原本文件功能,所以我们要把分片合成为原本的文件。我们只需要把分片按原本位置写入到文件中去。因为前面原理那一部我们已经讲到了,我们知道分块大小和分块序号,我就可以知道该分块在文件中的起始位置。所以这里使用RandomAccessFile是明智的,RandomAccessFile能在文件里面前后移动。但是在RandomAccessFile的绝大多数功能,已经被JDK1.4的NIO的“内存映射文件(memory-mapped files)”取代了。我在该项目中分别写了使用RandomAccessFile与MappedByteBuffer来合成文件。分别对应的方法是uploadFileRandomAccessFile和uploadFileByMappedByteBuffer。两个方法代码如下。 -``` - public void uploadFileRandomAccessFile(MultipartFileParam param) throws IOException { +```java + public void uploadFileRandomAccessFile(MultipartFileParam param) throws IOException { String fileName = param.getName(); String tempDirPath = finalDirPath + param.getMd5(); String tempFileName = fileName + "_tmp"; @@ -151,9 +130,9 @@ Spring Boot用来简化Spring应用的初始搭建以及开发过程,一个约 RandomAccessFile accessTmpFile = new RandomAccessFile(tmpFile, "rw"); long offset = CHUNK_SIZE * param.getChunk(); - //定位到该分片的偏移量 + // 定位到该分片的偏移量 accessTmpFile.seek(offset); - //写入该分片数据 + // 写入该分片数据 accessTmpFile.write(param.getFile().getBytes()); // 释放 accessTmpFile.close(); @@ -164,51 +143,48 @@ Spring Boot用来简化Spring应用的初始搭建以及开发过程,一个约 System.out.println("upload complete !!" + flag + " name=" + fileName); } } - + public void uploadFileByMappedByteBuffer(MultipartFileParam param) throws IOException { - String fileName = param.getName(); - String uploadDirPath = finalDirPath + param.getMd5(); - String tempFileName = fileName + "_tmp"; - File tmpDir = new File(uploadDirPath); - File tmpFile = new File(uploadDirPath, tempFileName); - if (!tmpDir.exists()) { - tmpDir.mkdirs(); - } - - RandomAccessFile tempRaf = new RandomAccessFile(tmpFile, "rw"); - FileChannel fileChannel = tempRaf.getChannel(); - - //写入该分片数据 - long offset = CHUNK_SIZE * param.getChunk(); - byte[] fileData = param.getFile().getBytes(); - MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, offset, fileData.length); - mappedByteBuffer.put(fileData); - // 释放 - FileMD5Util.freedMappedByteBuffer(mappedByteBuffer); - fileChannel.close(); - - boolean isOk = checkAndSetUploadProgress(param, uploadDirPath); - if (isOk) { - boolean flag = renameFile(tmpFile, fileName); - System.out.println("upload complete !!" + flag + " name=" + fileName); - } + String fileName = param.getName(); + String uploadDirPath = finalDirPath + param.getMd5(); + String tempFileName = fileName + "_tmp"; + File tmpDir = new File(uploadDirPath); + File tmpFile = new File(uploadDirPath, tempFileName); + if (!tmpDir.exists()) { + tmpDir.mkdirs(); } + + RandomAccessFile tempRaf = new RandomAccessFile(tmpFile, "rw"); + FileChannel fileChannel = tempRaf.getChannel(); + + // 写入该分片数据 + long offset = CHUNK_SIZE * param.getChunk(); + byte[] fileData = param.getFile().getBytes(); + MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, offset, fileData.length); + mappedByteBuffer.put(fileData); + // 释放 + FileMD5Util.freedMappedByteBuffer(mappedByteBuffer); + fileChannel.close(); + + boolean isOk = checkAndSetUploadProgress(param, uploadDirPath); + if (isOk) { + boolean flag = renameFile(tmpFile, fileName); + System.out.println("upload complete !!" + flag + " name=" + fileName); + } + } ``` - ### 秒传功能 - -  秒传功能,相信大家都体现过了,网盘上传的时候,发现上传的文件秒传了。其实原理稍微有研究过的同学应该知道,其实就是检验文件MD5,记录下上传到系统的文件的MD5,在一个文件上传前先获取文件内容MD5值或者部分取值MD5,然后在匹配系统上的数据。 -  *Breakpoint-http*实现秒传原理,客户端选择文件之后,点击上传的时候触发获取文件MD5值,获取MD5后调用系统一个接口(/index/checkFileMd5),查询该MD5是否已经存在(我在该项目中用redis来存储数据,用文件MD5值来作key,value是文件存储的地址。)接口返回检查状态,然后再进行下一步的操作。相信大家看代码就能明白了。 -  嗯,前端的MD5取值也是用了webuploader自带的功能,这还是个不错的工具。 - +**秒传功能**,相信大家都体验过了,网盘上传的时候,发现上传的文件秒传了。其实原理稍微有研究过的同学应该知道,其实就是检验文件MD5,记录下上传到系统的文件的MD5,在一个文件上传前先获取文件内容MD5值或者部分取值MD5,然后在匹配系统上的数据。 +*Breakpoint-http*实现秒传原理,客户端选择文件之后,点击上传的时候触发获取文件MD5值,获取MD5后调用系统一个接口(`/index/checkFileMd5`),查询该MD5是否已经存在(我在该项目中用redis来存储数据,用文件MD5值来作key,value是文件存储的地址。)接口返回检查状态,然后再进行下一步的操作。相信大家看代码就能明白了。前端的MD5取值也是用了webuploader自带的功能,这还是个不错的工具。 ### 断点续传 -  断点续传,就是在文件上传的过程中发生了中断,人为因素(暂停)或者不可抗力(断网或者网络差)导致了文件上传到一半失败了。然后在环境恢复的时候,重新上传该文件,而不至于是从新开始上传的。 -  前面也已经讲过,断点续传的功能是基于分块上传来实现的,把一个大文件分成很多个小块,服务端能够把每个上传成功的分块都落地下来,客户端在上传文件开始时调用接口快速验证,条件选择跳过某个分块。 -  实现原理,就是在每个文件上传前,就获取到文件MD5取值,在上传文件前调用接口(/index/checkFileMd5,没错也是秒传的检验接口)如果获取的文件状态是未完成,则返回所有的还没上传的分块的编号,然后前端进行条件筛算出哪些没上传的分块,然后进行上传。 -``` -/** +**断点续传**,就是在文件上传的过程中发生了中断,人为因素(暂停)或者不可抗力(断网或者网络差)导致了文件上传到一半失败了。然后在环境恢复的时候,重新上传该文件,而不至于是从新开始上传的。 +;前面也已经讲过,断点续传的功能是基于分块上传来实现的,把一个大文件分成很多个小块,服务端能够把每个上传成功的分块都落地下来,客户端在上传文件开始时调用接口快速验证,条件选择跳过某个分块。 +实现原理,就是在每个文件上传前,就获取到文件MD5取值,在上传文件前调用接口(/index/checkFileMd5,没错也是秒传的检验接口)如果获取的文件状态是未完成,则返回所有的还没上传的分块的编号,然后前端进行条件筛算出哪些没上传的分块,然后进行上传。 + +```java + /** * 秒传判断,断点判断 * * @return @@ -237,15 +213,32 @@ Spring Boot用来简化Spring应用的初始搭建以及开发过程,一个约 return new ResultVo<>(ResultStatus.ING_HAVE, missChunkList); } } -``` +``` + + ## 总结 -  身为一个具有拖延症的程序猿,写个文档及其不容易,这方面还是优待加强,写代码时间都还没写这个文档长,并且写了那么久还那么烂的文档。实在抱歉,望谅解。 -  项目的Bug和改进点,可在评论去留言或者在GitHub或者OSChina上以issue的方式直接提交给我,谢谢大家。 +身为一个具有拖延症的程序猿,写个文档及其不容易,这方面还是有待加强,写代码时间都还没写这个文档长,并且写了那么久还那么烂的文档。实在抱歉,望谅解。 +项目的Bug和改进点,可在评论去留言或者在GitHub或者OSChina上以issue的方式直接提交给我,谢谢大家。 + + + + +## 后续 +由于本项目使用的是Gradle+redis的方案,对于目前的一般项目而言,Gradle使用的还比较少,redis也是一种方式,传统的数据库,在使用之中还是有比较广泛的 +使用基础,如果当项目之中使用了数据库,或者不太想引入redis的时候,我们就可以采用mybatis存取相关的数据,在`store-mysql`的分支之中,我继续使用了 +Gradle的管理方式,但是就目前而言,其对于mapper.xml文件的打包,还有些许问题,而使用maven+mybatis(mybatis-plus)的方式在我的github +[springboot2.x-integration](https://github.com/prayjourney/springboot2.x-integration)的master分支之中集成了功能,并且可以正常 +运行,如果访问github网速过慢,可以访问gitee的[springboot2.x-integration](https://gitee.com/zuiguangyin123/springboot2.x-integration) +的master分支,同时如果有问题或者发现bug, 可以提issue给我,欢迎fork或者star。 + + + +## 更新记录 +当前默认分支是springboot2.x, 修改项目为springboot2+redis+maven的方式。 + -##参考文献 -[1]http://fex.baidu.com/webuploader/ -[2]http://www.zuidaima.com/blog/2819949848316928.htm -[3]https://my.oschina.net/feichexia/blog/212318 \ No newline at end of file +## 参考文献 +1. http://fex.baidu.com/webuploader/, 2. http://www.zuidaima.com/blog/2819949848316928.htm, 3. https://my.oschina.net/feichexia/blog/212318 \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 1059e49..0000000 --- a/build.gradle +++ /dev/null @@ -1,47 +0,0 @@ -group 'win.pangniu.learn' -version '1.0-SNAPSHOT' - -apply plugin: 'java' -apply plugin: 'war' -apply plugin: 'maven' -apply plugin: 'spring-boot' - -sourceCompatibility = 1.8 - -buildscript { - repositories { - mavenLocal() - mavenCentral() - jcenter() - } - dependencies { - classpath( - "org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE" - ) - } -} - -repositories { - mavenCentral() -} - -dependencies { - compile( - 'org.springframework.boot:spring-boot-starter-web', - 'org.springframework.boot:spring-boot-starter-data-redis' - ) - // https://mvnrepository.com/artifact/commons-io/commons-io - compile group: 'commons-io', name: 'commons-io', version: '2.5' - // https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload - compile group: 'commons-fileupload', name: 'commons-fileupload', version: '1.3.2' - // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 - compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.5' - //providedCompile("org.springframework.boot:spring-boot-starter-tomcat") - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile('org.springframework.boot:spring-boot-starter-test') -} - -// war -war { - archiveName 'bphttp.war' -} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a76aacd --- /dev/null +++ b/pom.xml @@ -0,0 +1,104 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.7.RELEASE + + + com.zgy.learn + Breakpoint-http + 0.0.1-SNAPSHOT + Breakpoint-http + Demo project for Spring Boot + + + 1.8 + UTF-8 + UTF-8 + 1.8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + commons-io + commons-io + 2.5 + + + org.apache.commons + commons-lang3 + 3.5 + + + commons-fileupload + commons-fileupload + 1.3.2 + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.springframework.boot + spring-boot-configuration-processor + + + org.projectlombok + lombok + + + + + + + + diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 9f4f3e8..0000000 --- a/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'breakpoint-http' - diff --git a/src/main/java/com/zgy/learn/BreakPointHttpApplication.java b/src/main/java/com/zgy/learn/BreakPointHttpApplication.java new file mode 100644 index 0000000..ef26bfb --- /dev/null +++ b/src/main/java/com/zgy/learn/BreakPointHttpApplication.java @@ -0,0 +1,13 @@ +package com.zgy.learn; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class BreakPointHttpApplication { + + public static void main(String[] args) { + SpringApplication.run(BreakPointHttpApplication.class, args); + } + +} diff --git a/src/main/java/win/pangniu/learn/controller/IndexController.java b/src/main/java/com/zgy/learn/controller/IndexController.java similarity index 79% rename from src/main/java/win/pangniu/learn/controller/IndexController.java rename to src/main/java/com/zgy/learn/controller/IndexController.java index 595b8c2..684f752 100644 --- a/src/main/java/win/pangniu/learn/controller/IndexController.java +++ b/src/main/java/com/zgy/learn/controller/IndexController.java @@ -1,5 +1,8 @@ -package win.pangniu.learn.controller; +package com.zgy.learn.controller; +import com.zgy.learn.param.MultipartFileParam; +import com.zgy.learn.service.StorageService; +import com.zgy.learn.utils.Constants; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; @@ -11,11 +14,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; -import win.pangniu.learn.param.MultipartFileParam; -import win.pangniu.learn.service.StorageService; -import win.pangniu.learn.utils.Constants; -import win.pangniu.learn.vo.ResultStatus; -import win.pangniu.learn.vo.ResultVo; +import com.zgy.learn.vo.ResultStatus; +import com.zgy.learn.vo.ResultVo; import javax.servlet.http.HttpServletRequest; import java.io.File; @@ -23,11 +23,6 @@ import java.util.LinkedList; import java.util.List; -/** - * 默认控制层 - * Created by wenwen on 2017/4/11. - * version 1.0 - */ @Controller @RequestMapping(value = "/index") public class IndexController { @@ -41,13 +36,16 @@ public class IndexController { private StorageService storageService; /** - * 秒传判断,断点判断 + * 秒传判断, 断点判断 * + * @param md5 * @return + * @throws IOException */ @RequestMapping(value = "checkFileMd5", method = RequestMethod.POST) @ResponseBody public Object checkFileMd5(String md5) throws IOException { + // 从redis之中获取MD5信息 Object processingObj = stringRedisTemplate.opsForHash().get(Constants.FILE_UPLOAD_STATUS, md5); if (processingObj == null) { return new ResultVo(ResultStatus.NO_HAVE); @@ -83,20 +81,18 @@ public Object checkFileMd5(String md5) throws IOException { public ResponseEntity fileUpload(MultipartFileParam param, HttpServletRequest request) { boolean isMultipart = ServletFileUpload.isMultipartContent(request); if (isMultipart) { - logger.info("上传文件start。"); + logger.info("上传文件start!"); try { // 方法1 - //storageService.uploadFileRandomAccessFile(param); - // 方法2 这个更快点 + // storageService.uploadFileRandomAccessFile(param); storageService.uploadFileByMappedByteBuffer(param); } catch (IOException e) { e.printStackTrace(); - logger.error("文件上传失败。{}", param.toString()); + logger.error("文件上传失败, {}", param.toString()); } - logger.info("上传文件end。"); + logger.info("上传文件end!"); } - return ResponseEntity.ok().body("上传成功。"); + return ResponseEntity.ok().body("上传成功!"); } - } diff --git a/src/main/java/com/zgy/learn/param/MultipartFileParam.java b/src/main/java/com/zgy/learn/param/MultipartFileParam.java new file mode 100644 index 0000000..76a68e9 --- /dev/null +++ b/src/main/java/com/zgy/learn/param/MultipartFileParam.java @@ -0,0 +1,38 @@ +package com.zgy.learn.param; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.springframework.web.multipart.MultipartFile; + +/** + * 分片上传包装 + */ +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class MultipartFileParam { + // 用户id + private String uid; + // 任务ID + private String id; + // 总分片数量 + private int chunks; + // 当前为第几块分片 + private int chunk; + // 当前分片大小 + private long size = 0L; + // 文件名 + private String name; + // 分片对象 + private MultipartFile file; + // MD5值 + private String md5; + +} diff --git a/src/main/java/win/pangniu/learn/service/StorageService.java b/src/main/java/com/zgy/learn/service/StorageService.java similarity index 84% rename from src/main/java/win/pangniu/learn/service/StorageService.java rename to src/main/java/com/zgy/learn/service/StorageService.java index e6cd87d..05d44ab 100644 --- a/src/main/java/win/pangniu/learn/service/StorageService.java +++ b/src/main/java/com/zgy/learn/service/StorageService.java @@ -1,12 +1,11 @@ -package win.pangniu.learn.service; +package com.zgy.learn.service; -import win.pangniu.learn.param.MultipartFileParam; +import com.zgy.learn.param.MultipartFileParam; import java.io.IOException; /** * 存储操作的service - * Created by 超文 on 2017/5/2. */ public interface StorageService { diff --git a/src/main/java/win/pangniu/learn/service/impl/StorageServiceImpl.java b/src/main/java/com/zgy/learn/service/impl/StorageServiceImpl.java similarity index 80% rename from src/main/java/win/pangniu/learn/service/impl/StorageServiceImpl.java rename to src/main/java/com/zgy/learn/service/impl/StorageServiceImpl.java index 97b167d..a540850 100644 --- a/src/main/java/win/pangniu/learn/service/impl/StorageServiceImpl.java +++ b/src/main/java/com/zgy/learn/service/impl/StorageServiceImpl.java @@ -1,17 +1,16 @@ -package win.pangniu.learn.service.impl; +package com.zgy.learn.service.impl; +import com.zgy.learn.param.MultipartFileParam; +import com.zgy.learn.service.StorageService; +import com.zgy.learn.utils.Constants; +import com.zgy.learn.utils.FileMD5Util; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.util.FileSystemUtils; -import win.pangniu.learn.param.MultipartFileParam; -import win.pangniu.learn.service.StorageService; -import win.pangniu.learn.utils.Constants; -import win.pangniu.learn.utils.FileMD5Util; import java.io.File; import java.io.IOException; @@ -24,19 +23,18 @@ import java.nio.file.Paths; /** - * Created by 超文 on 2017/5/2. + * 存储操作的service, 具体实现可以由redis, mysql, mongodb等来实现 */ +@Slf4j @Service public class StorageServiceImpl implements StorageService { - - private final Logger logger = LoggerFactory.getLogger(StorageServiceImpl.class); // 保存文件的根目录 - private Path rootPaht; + private Path rootPath; @Autowired private StringRedisTemplate stringRedisTemplate; - //这个必须与前端设定的值一致 + // 这个必须与前端设定的值一致 @Value("${breakpoint.upload.chunkSize}") private long CHUNK_SIZE; @@ -45,26 +43,26 @@ public class StorageServiceImpl implements StorageService { @Autowired public StorageServiceImpl(@Value("${breakpoint.upload.dir}") String location) { - this.rootPaht = Paths.get(location); + this.rootPath = Paths.get(location); } @Override public void deleteAll() { - logger.info("开发初始化清理数据,start"); - FileSystemUtils.deleteRecursively(rootPaht.toFile()); + log.info("开发初始化清理数据, start"); + FileSystemUtils.deleteRecursively(rootPath.toFile()); stringRedisTemplate.delete(Constants.FILE_UPLOAD_STATUS); stringRedisTemplate.delete(Constants.FILE_MD5_KEY); - logger.info("开发初始化清理数据,end"); + log.info("开发初始化清理数据, end"); } @Override public void init() { try { - Files.createDirectory(rootPaht); + Files.createDirectory(rootPath); } catch (FileAlreadyExistsException e) { - logger.error("文件夹已经存在了,不用再创建。"); + log.error("文件夹已经存在了, 不用再创建!"); } catch (IOException e) { - logger.error("初始化root文件夹失败。", e); + log.error("初始化root文件夹失败. ", e); } } @@ -81,9 +79,9 @@ public void uploadFileRandomAccessFile(MultipartFileParam param) throws IOExcept RandomAccessFile accessTmpFile = new RandomAccessFile(tmpFile, "rw"); long offset = CHUNK_SIZE * param.getChunk(); - //定位到该分片的偏移量 + // 定位到该分片的偏移量 accessTmpFile.seek(offset); - //写入该分片数据 + // 写入该分片数据 accessTmpFile.write(param.getFile().getBytes()); // 释放 accessTmpFile.close(); @@ -109,7 +107,7 @@ public void uploadFileByMappedByteBuffer(MultipartFileParam param) throws IOExce RandomAccessFile tempRaf = new RandomAccessFile(tmpFile, "rw"); FileChannel fileChannel = tempRaf.getChannel(); - //写入该分片数据 + // 写入该分片数据 long offset = CHUNK_SIZE * param.getChunk(); byte[] fileData = param.getFile().getBytes(); MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, offset, fileData.length); @@ -137,17 +135,17 @@ private boolean checkAndSetUploadProgress(MultipartFileParam param, String uploa String fileName = param.getName(); File confFile = new File(uploadDirPath, fileName + ".conf"); RandomAccessFile accessConfFile = new RandomAccessFile(confFile, "rw"); - //把该分段标记为 true 表示完成 + // 把该分段标记为 true 表示完成 System.out.println("set part " + param.getChunk() + " complete"); accessConfFile.setLength(param.getChunks()); accessConfFile.seek(param.getChunk()); accessConfFile.write(Byte.MAX_VALUE); - //completeList 检查是否全部完成,如果数组里是否全部都是(全部分片都成功上传) + // completeList 检查是否全部完成,如果数组里是否全部都是(全部分片都成功上传) byte[] completeList = FileUtils.readFileToByteArray(confFile); byte isComplete = Byte.MAX_VALUE; for (int i = 0; i < completeList.length && isComplete == Byte.MAX_VALUE; i++) { - //与运算, 如果有部分没有完成则 isComplete 不是 Byte.MAX_VALUE + // 与运算, 如果有部分没有完成则 isComplete 不是 Byte.MAX_VALUE isComplete = (byte) (isComplete & completeList[i]); System.out.println("check part " + i + " complete?:" + completeList[i]); } @@ -176,14 +174,14 @@ private boolean checkAndSetUploadProgress(MultipartFileParam param, String uploa * @return */ public boolean renameFile(File toBeRenamed, String toFileNewName) { - //检查要重命名的文件是否存在,是否是文件 + // 检查要重命名的文件是否存在,是否是文件 if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) { - logger.info("File does not exist: " + toBeRenamed.getName()); + log.info("File does not exist: " + toBeRenamed.getName()); return false; } String p = toBeRenamed.getParent(); File newFile = new File(p + File.separatorChar + toFileNewName); - //修改文件名 + // 修改文件名 return toBeRenamed.renameTo(newFile); } diff --git a/src/main/java/com/zgy/learn/utils/Constants.java b/src/main/java/com/zgy/learn/utils/Constants.java new file mode 100644 index 0000000..b259894 --- /dev/null +++ b/src/main/java/com/zgy/learn/utils/Constants.java @@ -0,0 +1,17 @@ +package com.zgy.learn.utils; + +/** + * 常量表 + */ +public interface Constants { + /** + * 保存文件所在路径的key,eg.FILE_MD5:1243jkalsjflkwaejklgjawe + */ + String FILE_MD5_KEY = "FILE_MD5:"; + + /** + * 保存上传文件的状态 + */ + String FILE_UPLOAD_STATUS = "FILE_UPLOAD_STATUS"; + +} diff --git a/src/main/java/win/pangniu/learn/utils/FileMD5Util.java b/src/main/java/com/zgy/learn/utils/FileMD5Util.java similarity index 96% rename from src/main/java/win/pangniu/learn/utils/FileMD5Util.java rename to src/main/java/com/zgy/learn/utils/FileMD5Util.java index 949b943..fba59b2 100644 --- a/src/main/java/win/pangniu/learn/utils/FileMD5Util.java +++ b/src/main/java/com/zgy/learn/utils/FileMD5Util.java @@ -1,7 +1,8 @@ -package win.pangniu.learn.utils; +package com.zgy.learn.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; import java.io.File; import java.io.FileInputStream; @@ -16,10 +17,9 @@ import java.security.PrivilegedAction; /** - * 文件md5值 - * Created by 超文 on 2016/10/10. - * version 1.0 + * MD5检测工具 */ +@Component public class FileMD5Util { private final static Logger logger = LoggerFactory.getLogger(FileMD5Util.class); diff --git a/src/main/java/win/pangniu/learn/vo/ResultStatus.java b/src/main/java/com/zgy/learn/vo/ResultStatus.java similarity index 89% rename from src/main/java/win/pangniu/learn/vo/ResultStatus.java rename to src/main/java/com/zgy/learn/vo/ResultStatus.java index cc30aed..a100f31 100644 --- a/src/main/java/win/pangniu/learn/vo/ResultStatus.java +++ b/src/main/java/com/zgy/learn/vo/ResultStatus.java @@ -1,11 +1,9 @@ -package win.pangniu.learn.vo; +package com.zgy.learn.vo; import com.fasterxml.jackson.annotation.JsonFormat; /** * 结果类型枚举 - * Created by 超文 on 2017/5/2. - * version 1.0 */ @JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum ResultStatus { diff --git a/src/main/java/com/zgy/learn/vo/ResultVo.java b/src/main/java/com/zgy/learn/vo/ResultVo.java new file mode 100644 index 0000000..cc05e8e --- /dev/null +++ b/src/main/java/com/zgy/learn/vo/ResultVo.java @@ -0,0 +1,36 @@ +package com.zgy.learn.vo; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * 返回结果包装对象 + */ +@Setter +@Getter +@ToString +@Accessors(chain = true) +public class ResultVo { + + private ResultStatus status; + + private String msg; + + private T data; + + public ResultVo(ResultStatus status) { + this(status, status.getReasonPhrase(), null); + } + + public ResultVo(ResultStatus status, T data) { + this(status, status.getReasonPhrase(), data); + } + + public ResultVo(ResultStatus status, String msg, T data) { + this.status = status; + this.msg = msg; + this.data = data; + } +} diff --git a/src/main/java/win/pangniu/learn/App.java b/src/main/java/win/pangniu/learn/App.java deleted file mode 100644 index 7dbbf59..0000000 --- a/src/main/java/win/pangniu/learn/App.java +++ /dev/null @@ -1,19 +0,0 @@ -package win.pangniu.learn; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.web.support.SpringBootServletInitializer; - -/** - * 启动类 - * Created by wenwen on 2017/4/11. - * version 1.0 - */ -@SpringBootApplication -public class App extends SpringBootServletInitializer { - - public static void main(String[] args) { - SpringApplication.run(App.class, args); - } - -} diff --git a/src/main/java/win/pangniu/learn/param/MultipartFileParam.java b/src/main/java/win/pangniu/learn/param/MultipartFileParam.java deleted file mode 100644 index e682b29..0000000 --- a/src/main/java/win/pangniu/learn/param/MultipartFileParam.java +++ /dev/null @@ -1,106 +0,0 @@ -package win.pangniu.learn.param; - - -import org.springframework.web.multipart.MultipartFile; - -/** - * Created by wenwen on 2017/4/16. - * version 1.0 - */ -public class MultipartFileParam { - - // 用户id - private String uid; - //任务ID - private String id; - //总分片数量 - private int chunks; - //当前为第几块分片 - private int chunk; - //当前分片大小 - private long size = 0L; - //文件名 - private String name; - //分片对象 - private MultipartFile file; - // MD5 - private String md5; - - public String getUid() { - return uid; - } - - public void setUid(String uid) { - this.uid = uid; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public int getChunks() { - return chunks; - } - - public void setChunks(int chunks) { - this.chunks = chunks; - } - - public int getChunk() { - return chunk; - } - - public void setChunk(int chunk) { - this.chunk = chunk; - } - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public MultipartFile getFile() { - return file; - } - - public void setFile(MultipartFile file) { - this.file = file; - } - - public String getMd5() { - return md5; - } - - public void setMd5(String md5) { - this.md5 = md5; - } - - @Override - public String toString() { - return "MultipartFileParam{" + - "uid='" + uid + '\'' + - ", id='" + id + '\'' + - ", chunks=" + chunks + - ", chunk=" + chunk + - ", size=" + size + - ", name='" + name + '\'' + - ", file=" + file + - ", md5='" + md5 + '\'' + - '}'; - } -} diff --git a/src/main/java/win/pangniu/learn/utils/Constants.java b/src/main/java/win/pangniu/learn/utils/Constants.java deleted file mode 100644 index 72db398..0000000 --- a/src/main/java/win/pangniu/learn/utils/Constants.java +++ /dev/null @@ -1,31 +0,0 @@ -package win.pangniu.learn.utils; - -import java.util.HashMap; -import java.util.Map; - -/** - * 常量表 - * Created by 超文 on 2017/05/02. - * version 1.0 - */ -public interface Constants { - /** - * 异常信息统一头信息
- * 非常遗憾的通知您,程序发生了异常 - */ - public static final String Exception_Head = "boom。炸了。"; - /** - * 缓存键值 - */ - public static final Map, String> cacheKeyMap = new HashMap<>(); - /** - * 保存文件所在路径的key,eg.FILE_MD5:1243jkalsjflkwaejklgjawe - */ - public static final String FILE_MD5_KEY = "FILE_MD5:"; - /** - * 保存上传文件的状态 - */ - public static final String FILE_UPLOAD_STATUS = "FILE_UPLOAD_STATUS"; - - -} diff --git a/src/main/java/win/pangniu/learn/vo/ResultVo.java b/src/main/java/win/pangniu/learn/vo/ResultVo.java deleted file mode 100644 index 88a2b8e..0000000 --- a/src/main/java/win/pangniu/learn/vo/ResultVo.java +++ /dev/null @@ -1,62 +0,0 @@ -package win.pangniu.learn.vo; - -/** - * 统一返回结果pojo - * Created by wenwen on 2017/4/23. - * version 1.0 - */ -public class ResultVo { - - private ResultStatus status; - - private String msg; - - private T data; - - public ResultVo(ResultStatus status) { - this(status, status.getReasonPhrase(), null); - } - - public ResultVo(ResultStatus status, T data) { - this(status, status.getReasonPhrase(), data); - } - - public ResultVo(ResultStatus status, String msg, T data) { - this.status = status; - this.msg = msg; - this.data = data; - } - - public ResultStatus getStatus() { - return status; - } - - public void setStatus(ResultStatus status) { - this.status = status; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - - public T getData() { - return data; - } - - public void setData(T data) { - this.data = data; - } - - @Override - public String toString() { - return "ResultVo{" + - "status=" + status + - ", msg='" + msg + '\'' + - ", data=" + data + - '}'; - } -} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index c6a1ef0..d003a4d 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,20 +1,23 @@ -#开发环境 breakpoint: upload: dir: D:/data0/uploads/ - #1024*1024=1 048 576,5M=5 242 880 + #1024*1024=1048576 5M=5242880 chunkSize: 5 242 880 - spring: redis: host: 127.0.0.1 port: 6379 - pool: - max-active: 30 - max-idle: 10 - max-wait: 10000 - timeout: 0 + timeout: 300000 + jedis: + pool: + max-idle: 30 + min-idle: 0 + max-active: 8 + max-wait: -1ms http: + servlet: multipart: - max-file-size: 5MB + max-file-size: 10MB max-request-size: 100MB + thymeleaf: + cache: false diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c230f3d..53cff54 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,26 +1,12 @@ -# Server settings (ServerProperties) server: port: 9090 - contextPath: / - # Tomcat specifics - tomcat: - # 打开Tomcat的Access日志,并可以设置日志格式的方法 - # accessLogEnabled: false - protocolHeader: x-forwarded-proto - remoteIpHeader: x-forwarded-for - basedir: - backgroundProcessorDelay: 30 - session: - timeout: 30 # secs + servlet: + session: + timeout: 30 -#log logging: config: classpath:logback.xml - level: info -# 默认的profile为dev,其他环境通过指定启动参数使用不同的profile,比如: -# 测试环境:java -jar my-spring-boot.jar --spring.profiles.active=test -# 生产环境:java -jar my-spring-boot.jar --spring.profiles.active=prod spring: profiles: active: dev \ No newline at end of file diff --git a/src/main/resources/public/index.html b/src/main/resources/public/index.html deleted file mode 100644 index f6d90c2..0000000 --- a/src/main/resources/public/index.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - 断点上传 - - - - -
- -
-
-
选择大文件
- -
-
- - - - - - \ No newline at end of file diff --git a/src/main/resources/static/css/webuploader.css b/src/main/resources/static/css/webuploader.css index 12f451f..dfb1db5 100644 --- a/src/main/resources/static/css/webuploader.css +++ b/src/main/resources/static/css/webuploader.css @@ -1,28 +1,34 @@ .webuploader-container { - position: relative; + position: relative; +/ / position: relative; } + .webuploader-element-invisible { - position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ - clip: rect(1px,1px,1px,1px); + position: absolute !important; + clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ + clip: rect(1px, 1px, 1px, 1px); } + .webuploader-pick { - position: relative; - display: inline-block; - cursor: pointer; - background: #00b7ee; - padding: 10px 15px; - color: #fff; - text-align: center; - border-radius: 3px; - overflow: hidden; + position: relative; + display: inline-block; + cursor: pointer; + background: #00b7ee; + padding-left: 5px; + padding-right: 5px; +/ / padding: - 5 px - 5 px; + color: #fff; + text-align: center; + border-radius: 3px; + overflow: hidden; } + .webuploader-pick-hover { - background: #00a2d4; + background: #00a2d4; } .webuploader-pick-disable { - opacity: 0.6; - pointer-events:none; + opacity: 0.6; + pointer-events: none; } diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..af82056 --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,209 @@ + + + + + 断点上传 + + + + + + +
+ +
+ +
+
选择大文件
+ + + + +
+
+ + + + \ No newline at end of file diff --git a/src/main/webapp/index2.jsp b/src/main/webapp/index2.jsp deleted file mode 100644 index 68ec1f7..0000000 --- a/src/main/webapp/index2.jsp +++ /dev/null @@ -1,43 +0,0 @@ -<%@ page import="java.util.HashMap" %> -<%@ page import="java.util.Map" %><%-- - Created by IntelliJ IDEA. - User: wenwen - Date: 2017/4/11 - Time: 22:03 - To change this template use File | Settings | File Templates. ---%> -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<% - // 设置请求的编码为UTF-8; - request.setCharacterEncoding("UTF-8"); - // 设置响应的编码为UTF-8 - response.setCharacterEncoding("UTF-8"); - - // 帐号密码数据准备 - Map accountMap = new HashMap<>(); - accountMap.put("ftpuser", "password"); - - // 获取参数 - String userName = request.getParameter("userName"); - System.err.println(userName); - String result; - if (userName == null || "".equals(userName.trim())) { - result = "{\"code\":101,\"msg\":\"参数为空\"}"; - } else { - String password = accountMap.get(userName); - if (password == null || "".equals(password)) { - result = "{\"code\":102,\"msg\":\"结果为空。\"}"; - } else { - char[] array = password.toCharArray(); - for (int i = 0; i < array.length; i++) { - array[i] = (char) (array[i] ^ 20160831); - } - System.out.println("结果如下:"); - System.out.println(new String(array));//输出加密或者解密结果 - result = "{\"code\":100,\"msg\":'成功',\"data\":\"" + new String(array) + "\"}"; - } - } - - // 将处理后的结果返回给客户端 - response.getWriter().write("" + result); -%> \ No newline at end of file diff --git a/src/test/java/win/pangniu/learn/test/AppTest.java b/src/test/java/com/zgy/learn/test/BreakPointHttpApplicationTest.java similarity index 85% rename from src/test/java/win/pangniu/learn/test/AppTest.java rename to src/test/java/com/zgy/learn/test/BreakPointHttpApplicationTest.java index 7e9bc22..d994493 100644 --- a/src/test/java/win/pangniu/learn/test/AppTest.java +++ b/src/test/java/com/zgy/learn/test/BreakPointHttpApplicationTest.java @@ -1,20 +1,22 @@ -package win.pangniu.learn.test; +package com.zgy.learn.test; -import org.junit.Test; + +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SessionCallback; import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.test.context.ActiveProfiles; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLConnection; import java.util.List; -public class AppTest extends BaseTest { + +@SpringBootTest +@ActiveProfiles(value = "dev") +public class BreakPointHttpApplicationTest { @Autowired private StringRedisTemplate stringRedisTemplate; diff --git a/src/test/java/win/pangniu/learn/test/BaseTest.java b/src/test/java/win/pangniu/learn/test/BaseTest.java deleted file mode 100644 index c7750db..0000000 --- a/src/test/java/win/pangniu/learn/test/BaseTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package win.pangniu.learn.test; - -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Created by 超文 on 2016/10/20. - */ -// 1.4 -/*@RunWith(SpringJUnit4ClassRunner.class) // SpringJUnit支持,由此引入Spring-Test框架支持! -//@SpringApplicationConfiguration(classes = App.class) // 指定我们SpringBoot工程的Application启动类 -//@ContextConfiguration(locations = {"classpath:spring-context-*.xml"}) -@WebAppConfiguration // 由于是Web项目,Junit需要模拟ServletContext,因此我们需要给我们的测试类加上@WebAppConfiguration。 -@SpringBootTest*/ - -// spring boot 1.5用法 -@RunWith(SpringRunner.class) -@SpringBootTest -public class BaseTest { -}