提交 376b9355 authored 作者: zhangshuang's avatar zhangshuang

swagger和断点续传

上级 466a9771
......@@ -17,6 +17,13 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.zjty</groupId>
<artifactId>efs-misc</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
......
......@@ -2,8 +2,15 @@ package com.zjty.efs.ftp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication
@ComponentScan(basePackages = {
"com.zjty.efs.misc",
"com.zjty.efs.ftp"
})
@EnableSwagger2
public class FtpApplication {
public static void main(String[] args) {
SpringApplication.run(FtpApplication.class, args);
......
package com.zjty.efs.ftp.controller;
import com.zjty.efs.ftp.service.FileDownLoadService;
import com.zjty.efs.misc.config.AutoDocument;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -10,13 +15,22 @@ import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@AutoDocument
@RestController
@RequestMapping("/file")
@Api(tags = "文件下载",description = "文件下载")
public class FileDownLoadController {
@Autowired
private FileDownLoadService fileDownLoadService;
@GetMapping("/downLoad/{fileName}")
@GetMapping("/download/{fileName}")
@ApiOperation(value = "文件下载",notes = "通过文件名称下载文件")
@ApiImplicitParams({
@ApiImplicitParam(name = "fileName",value = "文件名称",paramType = "String",example = "1.zip",required = true),
@ApiImplicitParam(name = "response",value = "响应对象",paramType = "HttpServletResponse",required = true),
@ApiImplicitParam(name = "request",value = "请求对象",paramType = "HttpServletRequest",required = true),
})
public void fileDownLoad(@PathVariable("fileName") String fileName, HttpServletResponse response, HttpServletRequest request){
fileDownLoadService.fileDownLoad(fileName,response,request);
}
......
......@@ -2,15 +2,21 @@ package com.zjty.efs.ftp.controller;
import com.zjty.efs.ftp.base.response.ServerResponse;
import com.zjty.efs.ftp.service.FileUploadService;
import com.zjty.efs.misc.config.AutoDocument;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
@AutoDocument
@RestController
@RequestMapping("/file")
@Api(tags = "文件上传", description = "文件上传")
public class FileUploadController {
@Autowired
......@@ -22,4 +28,70 @@ public class FileUploadController {
return fileUploadService.fileUpload(httpServletRequest);
}
/**
* @author zs
* 检查文件存在与否
*/
@PostMapping("/checkFile")
@ResponseBody
@ApiOperation(value = "文件检查",notes = "检查文件存在与否")
@ApiImplicitParams({
@ApiImplicitParam(name = "md5File",value = "md5生成的文件名称",paramType = "String",required = true)
})
public ServerResponse checkFile(@RequestParam(value = "md5File") String md5File) {
return fileUploadService.checkFile(md5File);
}
/**
* @author zs
* 检查分片存不存在
*/
@PostMapping("/checkChunk")
@ResponseBody
@ApiOperation(value = "分片检查",notes = "检查分片存不存在")
@ApiImplicitParams({
@ApiImplicitParam(name = "md5File",value = "md5生成的文件名称",paramType = "String",required = true),
@ApiImplicitParam(name = "chunk",value = "分片名称",paramType = "Integer",required = true)
})
public ServerResponse checkChunk(@RequestParam(value = "md5File") String md5File,
@RequestParam(value = "chunk") Integer chunk) {
return fileUploadService.checkChunk(md5File,chunk);
}
/**
* @author zs
* 修改上传
*/
@PostMapping("/upload")
@ResponseBody
@ApiOperation(value = "文件上传",notes = "上传文件,支持断点续传")
@ApiImplicitParams({
@ApiImplicitParam(name = "md5File",value = "md5生成的文件名称",paramType = "String",required = true),
@ApiImplicitParam(name = "chunk",value = "分片名称",paramType = "Integer",required = true),
@ApiImplicitParam(name = "file",value = "文件",paramType = "MultipartFile",required = true)
})
public ServerResponse upload(@RequestParam(value = "file") MultipartFile file,
@RequestParam(value = "md5File") String md5File,
@RequestParam(value = "chunk",required= false) Integer chunk) { //第几片,从0开始
return fileUploadService.upload(file,md5File,chunk);
}
/**
* @author zs
* 合成分片
*/
@PostMapping("/merge")
@ResponseBody
@ApiOperation(value = "合成分片",notes = "合成所有分片")
@ApiImplicitParams({
@ApiImplicitParam(name = "md5File",value = "md5生成的文件名称",paramType = "String",required = true),
@ApiImplicitParam(name = "chunk",value = "最后的分片名称",paramType = "Integer",required = true),
@ApiImplicitParam(name = "file",value = "文件",paramType = "MultipartFile",required = true)
})
public ServerResponse merge(@RequestParam(value = "chunks",required =false) Integer chunks,
@RequestParam(value = "md5File") String md5File,
@RequestParam(value = "name") String name){
return fileUploadService.merge(chunks,md5File,name);
}
}
......@@ -2,6 +2,7 @@ package com.zjty.efs.ftp.service;
import com.zjty.efs.ftp.base.response.ServerResponse;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
......@@ -14,4 +15,28 @@ public interface FileUploadService {
*/
ServerResponse fileUpload(HttpServletRequest request);
/**
* @author zs
* 检查文件存在与否
*/
ServerResponse checkFile(String md5File);
/**
* @author zs
* 检查分片存不存在
*/
ServerResponse checkChunk(String md5File, Integer chunk);
/**
* @author zs
* 修改上传
*/
ServerResponse upload(MultipartFile file, String md5File, Integer chunk);
/**
* @author zs
* 合成分片
*/
ServerResponse merge(Integer chunks, String md5File, String name);
}
......@@ -2,12 +2,16 @@ package com.zjty.efs.ftp.service.impl;
import com.zjty.efs.ftp.service.FileDownLoadService;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import static javax.xml.transform.OutputKeys.ENCODING;
@Service
@Slf4j
......@@ -16,7 +20,16 @@ public class FileDownLoadServiceImpl implements FileDownLoadService {
@Value("${file.address}")
private String fileAddress;
@Override
/**
* 下载服务器已存在的文件
*
* @param httpServletRequest
* 请求对象
* @param response
* 响应对象
* @param fileName
* 文件名称(绝对)
*/
public void fileDownLoad(String fileName, HttpServletResponse response, HttpServletRequest httpServletRequest) {
if(fileName != null){
response.setHeader("content-type", "application/octet-stream");
......@@ -67,6 +80,89 @@ public class FileDownLoadServiceImpl implements FileDownLoadService {
}
}
/**
* 下载服务器已存在的文件
*
* @param request
* 请求对象
* @param response
* 响应对象
* @param fileName
* 文件名称(绝对)
*/
public void download(String fileName, HttpServletResponse response, HttpServletRequest request) {
File proposeFile = new File(fileAddress + "/" + fileName);
log.info("下载文件路径:" + proposeFile.getPath());
setFileDownloadHeader(request,response,fileName);
InputStream inputStream = null;
OutputStream bufferOut = null;
try {
// 设置响应报头
long fSize = proposeFile.length();
response.setContentType(request.getServletContext().getMimeType(fileName));
// Content-Disposition: attachment; filename=WebGoat-OWASP_Developer-5.2.zip
response.setHeader("Content-Disposition", "attachment;filename="+new String(fileName.getBytes("utf-8"),"utf-8"));
// Accept-Ranges: bytes
response.setHeader("Accept-Ranges", "bytes");
long pos = 0, last = fSize - 1, sum = 0;//pos开始读取位置; last最后读取位置; sum记录总共已经读取了多少字节
if (null != request.getHeader("Range")) {
// 断点续传
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
try {
// 情景一:RANGE: bytes=2000070- 情景二:RANGE: bytes=2000070-2000970
String numRang = request.getHeader("Range").replaceAll("bytes=", "");
String[] strRange = numRang.split("-");
if (strRange.length == 2) {
pos = Long.parseLong(strRange[0].trim());
last = Long.parseLong(strRange[1].trim());
} else {
pos = Long.parseLong(numRang.replaceAll("-", "").trim());
}
} catch (NumberFormatException e) {
log.error(request.getHeader("Range") + " is not Number!");
pos = 0;
}
}
long rangLength = last - pos + 1;// 总共需要读取的字节
// Content-Range: bytes 10-1033/304974592
String contentRange = new StringBuffer("bytes ").append(pos).append("-").append(last).append("/").append(fSize).toString();
response.setHeader("Content-Range", contentRange);
// Content-Length: 1024
response.addHeader("Content-Length", String.valueOf(rangLength));
// 跳过已经下载的部分,进行后续下载
bufferOut = new BufferedOutputStream(response.getOutputStream());
inputStream = new BufferedInputStream(new FileInputStream(proposeFile));
inputStream.skip(pos);
byte[] buffer = new byte[1024];
int length = 0;
while (sum < rangLength) {
length = inputStream.read(buffer, 0, ((rangLength - sum) <= buffer.length ? ((int) (rangLength - sum)) : buffer.length));
sum = sum + length;
bufferOut.write(buffer, 0, length);
}
} catch (Throwable e) {
if (e instanceof ClientAbortException) {
// 浏览器点击取消
log.info("用户取消下载!");
} else {
log.error("下载文件失败....");
e.printStackTrace();
}
} finally {
try {
if (bufferOut != null) {
bufferOut.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 解决中文名称
* @param request
......
......@@ -76,9 +76,116 @@ public class FileUploadServiceImpl implements FileUploadService {
}
}
}else {
log.info("上传失败,请重新确定key值,重新上传");
log.error("上传失败,请重新确定key值,重新上传");
return ServerResponse.error("上传失败,请重新确定key值,重新上传");
}
return ServerResponse.success(fileReturns);
}
@Override
public ServerResponse checkFile(String md5File) {
Boolean exist = false;
//实际项目中,这个md5File唯一值,应该保存到数据库或者缓存中,通过判断唯一值存不存在,来判断文件存不存在,这里我就不演示了
/*if(true) {
exist = true;
}*/
return ServerResponse.success(exist);
}
@Override
public ServerResponse checkChunk(String md5File, Integer chunk) {
Boolean exist = false;
//String path = "D:/webuploader/"+md5File+"/";
String chunkPath = fileAddress + "/" + md5File + "/";//分片存放目录
String chunkName = chunk+ ".tmp";//分片名
File file = new File(chunkPath + chunkName);
if (file.exists()) {
exist = true;
}
return ServerResponse.success(exist);
}
@Override
public ServerResponse upload(MultipartFile file, String md5File, Integer chunk) {
//String path = "D:/webuploader/"+md5File+"/";
String chunkPath = fileAddress + "/" + md5File + "/";//分片存放目录
File dirFile = new File(chunkPath);
if (!dirFile.exists()) {//目录不存在,创建目录
dirFile.mkdirs();
}
String chunkName;
if(chunk == null) {//表示是小文件,还没有一片
chunkName = "0.tmp";
}else {
chunkName = chunk+ ".tmp";
}
String filePath = chunkPath + chunkName;
File saveFile = new File(filePath);
try {
if (!saveFile.exists()) {
saveFile.createNewFile();//文件不存在,则创建
}
file.transferTo(saveFile);//将文件保存
} catch (IOException e) {
return ServerResponse.error("false");
}
return ServerResponse.success("true");
}
@Override
public ServerResponse merge(Integer chunks, String md5File, String name) {
FileOutputStream fileOutputStream = null; //合成后的文件
try {
fileOutputStream = new FileOutputStream(fileAddress + "/" + name);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
byte[] buf = new byte[1024];
for(long i = 0;i < chunks;i++) {
String chunkFile = i+".tmp";
File file = new File(fileAddress + "/" + md5File+"/" + chunkFile);
InputStream inputStream = new FileInputStream(file);
int len = 0;
while((len = inputStream.read(buf)) != -1){
fileOutputStream.write(buf,0,len);
}
inputStream.close();
}
//删除md5目录,及临时文件
deleteFile(new File(fileAddress + "/" + md5File));
} catch (Exception e) {
return ServerResponse.error("false");
}finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return ServerResponse.success("true");
}
public static void deleteFile(File file){
//判断文件不为null或文件目录存在
if (file == null || !file.exists()){
System.out.println("文件删除失败,请检查文件路径是否正确");
return;
}
//取得这个目录下的所有子文件对象
File[] files = file.listFiles();
//遍历该目录下的文件对象
for (File f: files){
//打印文件名
//判断子目录是否存在子目录,如果是文件则删除
if (f.isDirectory()){
deleteFile(f);
}else {
f.delete();
}
}
//删除空文件夹 for循环已经把上一层节点的目录清空。
file.delete();
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论