sharp-fileupload客户端Restful(四)
客户端api可以用来上传、下载、浏览文件等操作,由 DocumentService
完成服务操作。DocumentService
主要工作:
- 管理数据库维护元数据
FileStore
来完成存储- 依赖
ImageService
完成图片类型的浏览
准备工作
创建表
CREATE TABLE `sys_document` (
`id` bigint(20) NOT NULL,
`name` varchar(255) NOT NULL,
`extension` varchar(16) DEFAULT NULL,
`content_type` varchar(16) DEFAULT NULL,
`size` int(11) DEFAULT NULL,
`group_name` varchar(255) NOT NULL,
`path` varchar(255) NOT NULL,
`created_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
添加依赖
<dependency>
<groupId>com.rick.db</groupId>
<artifactId>sharp-database</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
配置数据库
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/fastdfs?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2b8
username: root
password: jkxyx205
接口测试
批量上传
- 请求
POST /documents/upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="/Users/rick/Desktop/StockSnap_STDZONQNPW.jpg
Content-Disposition: form-data; name="file"; filename="/Users/rick/Desktop/java技能树.jpg
------WebKitFormBoundary7MA4YWxkTrZu0gW--
- 后端处理
@PostMapping("/upload")
public Result<List<?>> fileUpload(MultipartHttpServletRequest multipartRequest) throws IOException {
return ResultUtils.success(documentService.store(FileMetaUtils.parse(multipartRequest, UPLOAD_NAME), "upload"));
}
@PostMapping("/upload2")
public Result<List<?>> fileUpload2(@RequestParam(UPLOAD_NAME) List<MultipartFile> fileList) throws IOException {
return ResultUtils.success(documentService.store(FileMetaUtils.parse(fileList), "upload"));
}
@PostMapping("/upload3")
public Result<List<?>> fileUpload3(@RequestBody List<FileMeta> fileMetaList) throws IOException {
return ResultUtils.success(documentService.store(fileMetaList, "upload"));
}
- 响应
{
"success": true,
"code": 0,
"msg": "OK",
"data": [
{
"name": "StockSnap_STDZONQNPW",
"extension": "jpg",
"contentType": "image/jpeg",
"size": 30079171,
"groupName": "upload",
"path": "475304840076365824.jpg",
"url": "http://localhost:7892/upload/475304840076365824.jpg",
"id": 475304840302858240,
"createdAt": "2021-10-01T04:24:42.474Z",
"fullName": "StockSnap_STDZONQNPW.jpg",
"fullPath": "upload/475304840076365824.jpg"
},
{
"name": "java技能树",
"extension": "jpg",
"contentType": "image/jpeg",
"size": 337707,
"groupName": "upload",
"path": "475304840239943680.jpg",
"url": "http://localhost:7892/upload/475304840239943680.jpg",
"id": 475304840332218368,
"createdAt": "2021-10-01T04:24:42.481Z",
"fullName": "java技能树.jpg",
"fullPath": "upload/475304840239943680.jpg"
}
]
}
查看信息
- 请求
GET /documents/475304840302858240 HTTP/1.1
- 后端处理
/**
* 文件详情
* @return
*/
@GetMapping(value = "/{id}")
public Result<Document> documentInfo(@PathVariable Long id) {
return ResultUtils.success(documentService.findById(id));
}
- 响应
{
"success": true,
"code": 0,
"msg": "OK",
"data": {
"name": "StockSnap_STDZONQNPW",
"extension": "jpg",
"contentType": "image/jpeg",
"size": 30079171,
"groupName": "upload",
"path": "475304840076365824.jpg",
"url": "http://localhost:7892/upload/475304840076365824.jpg",
"id": 475304840302858240,
"createdAt": "2021-10-01T04:24:42Z",
"fullName": "StockSnap_STDZONQNPW.jpg",
"fullPath": "upload/475304840076365824.jpg"
}
}
下载
- 请求
# 单文件下载
http://localhost:8080/documents/download?id=475308098194935808
# 批量下载
http://localhost:8080/documents/download?id=475308098194935808&id=475308098169769984
- 后端处理
@GetMapping(value = "/download/{id}")
public void download(HttpServletRequest request, HttpServletResponse response, @PathVariable long id) throws IOException {
documentService.download(request, response , id);
}
@GetMapping(value = "/download")
public void download(HttpServletRequest request, HttpServletResponse response, @RequestParam(name = "id") long[] ids) throws IOException {
documentService.download(request, response, ids);
}
浏览
- 请求
# 客户端跳转
http://localhost:8080/documents/preview/475308098194935808
# 图片进行处理
http://localhost:8080/documents/preview2/475308098194935808?rw=9&rh=5
# office预览必须带上文件扩展名
https://view.officeapps.live.com/op/view.aspx?src=http%3A%2F%2Fa923-112-87-216-2.ngrok.io%2Fdocuments%2Fpreview2%2F477893013692383232/小程序部署准备工作.docx
https://view.officeapps.live.com/op/view.aspx?src=http%3A%2F%2Fa923-112-87-216-2.ngrok.io%2Fdocuments%2Fpreview2%2F477897073204035588/演示.pptx
https://view.officeapps.live.com/op/view.aspx?src=http%3A%2F%2Fa923-112-87-216-2.ngrok.io%2Fdocuments%2Fpreview2%2F477897073204035587/需求.xlsx
Note: the
- 后端处理
/**
* 预览文件: 通过nginx,这种访问速度会更快些
* http://localhost:8080/documents/preview/475029213054144512
* @return
* @throws IOException
*/
@GetMapping(value = "/preview/{id:\\d+}")
public void view(HttpServletResponse response, @PathVariable Long id) throws IOException {
response.sendRedirect(documentService.getURL(id));
}
/**
* 预览普通文件,将文件写到流中
* http://localhost:8080/documents/preview2/475029213054144512
* 预览office,必须有后缀docx/xlsx/pptx,否则会File not found
* https://view.officeapps.live.com/op/view.aspx?src=http%3A%2F%2Fa923-112-87-216-2.ngrok.io%2Fdocuments%2Fpreview2%2F477896371325009920/hello.docx
* @return
* @throws IOException
*/
@GetMapping(value = {"/preview2/{id:\\d+}", "/preview2/{id:\\d+}/{fileName}.{extension:(?i)docx|xlsx|pptx}"})
public void view2(HttpServletRequest request, HttpServletResponse response, @PathVariable Long id, ImageParam imageParam) throws IOException {
Document document = documentService.findById(id);
documentService.preview(id, imageParam, HttpServletResponseUtils.getOutputStreamAsView(request, response, document.getFullName()));
}
重命名
- 请求
PUT /documents/475029213070921728/rename?name=hello world HTTP/1.1```
- 后端处理
@PutMapping("/{id}/rename")
public Result rename(@PathVariable Long id, String name) {
documentService.rename(id, name);
return ResultUtils.success();
}
删除
- 请求
DELETE /documents/475010776193994752 HTTP/1.1
- 响应
{
"success": true,
"code": 0,
"msg": "OK"
}
- 后端处理
@DeleteMapping(value = "/{id}")
public Result deleteDocument(@PathVariable Long id) {
documentService.delete(id);
return ResultUtils.success();
}
- 响应
{
"success": true,
"code": 0,
"msg": "OK"
}
sharp-fileupload图片处理ImageService(三)
创建名字图片
方法
/**
* 创建名字图片
* @param text 张三
* @param groupName 存储
* @param storeName 存储的文件名,可能是用户的id
* @return
* @throws IOException
*/
public String createImage(String text, String groupName, String storeName) throws IOException
测试
String url = imageService.createImage("张三", "header");
String url = imageService.createImage("张三", "header", "Rick");
输出
http://localhost:7892/header/475290263729115136.png
http://localhost:7892/header/Rick.png
图片处理
public FileMeta cropPic(FileMeta fileMeta, ImageParam imageParam) throws IOException;
测试
@Test
@Order(1)
public void testCropPic() throws IOException {
File file = new File("/Users/rick/jkxyx205/tmp/fileupload/demo/1.jpg");
FileMeta fileMeta = FileMetaUtils.parse(file);
// 裁剪9:5
ImageParam imageParam = new ImageParam();
imageParam.setRw(9);
imageParam.setRh(5);
FileMeta cropPicFileMeta = imageService.cropPic(fileMeta, imageParam);
// 将裁剪的存储到磁盘
List<? extends FileMeta> crop = fileStore.storeFileMeta(Arrays.asList(cropPicFileMeta), "crop");
System.out.println(crop.get(0).getUrl());
}
图片浏览
依赖图片裁剪,通过Response的输出流实现图片浏览的功能
public void write(FileMeta fileMeta, ImageParam imageParam, OutputStream os) throws IOException
测试
/**
* http://localhost:8080/images/475036437923139584?p=0&rw=9&rh=5&r=30&w=500
* 原图按9:5裁剪,旋转30度,宽度500像素
* 图片查看
* @param request
* @param response
* @param imageParam
*/
@GetMapping("/{id}")
public void preview(HttpServletRequest request, HttpServletResponse response, @PathVariable("id") Long id, ImageParam imageParam) throws IOException {
Document document = documentService.findById(id);
imageService.write(document, imageParam, HttpServletResponseUtils.getOutputStreamAsView(request, response, document.getFullName()));
}
sharp-fileupload文件存储FileStore(二)
FileStore
依赖 inputStreamStore
,同时实现了inputStreamStore
接口。主要是处理文件文件上传。
方法
- 处理web端文件请求
public List<? extends FileMeta> upload(List<MultipartFile> multipartFileList, String groupName);
- 处理本地文件
public List<? extends FileMeta> storeFiles(List<File> fileList, String groupName)
- 处理byte[]的文件
FileMeta#data是一个字节数组
public List<? extends FileMeta> storeFileMeta(List<? extends FileMeta> fileMetaList, String groupName)
FileMeta.java
@Getter
@Setter
public class FileMeta {
private String name;
private String extension;
private String contentType;
private Long size;
private String groupName;
private String path;
@Transient
@JsonIgnore
private byte[] data;
@Transient
private String url;
/**
* 获取完整名称
* @return
*/
public String getFullName() {
if (StringUtils.isBlank(this.getExtension())) {
return this.getName();
}
return this.getName() + (StringUtils.isEmpty(this.getExtension()) ? "" : "." + this.getExtension());
}
public String getFullPath() {
return this.getGroupName() + "/" + getPath();
}
/**
* 设置完整名称
* @param fullName
*/
public void setFullName(String fullName) {
String fileName = com.rick.common.util.StringUtils.stripFilenameExtension(fullName);
String fileExt = com.rick.common.util.StringUtils.getFilenameExtension(fullName);
setName(fileName);
setExtension(fileExt);
}
}
测试
@Test
public void testStore() throws IOException {
File file = new File("/Users/rick/jkxyx205/tmp/fileupload/demo/1.jpg");
FileMeta fileMeta = FileMetaUtils.parse(file);
fileStore.storeFileMeta(Lists.newArrayList(fileMeta), "upload").get(0);
}
sharp-fileupload流存储InputStreamStore(一)
简介
sharp-fileupload
可以上传文件,对图片的处理(裁剪、选装、缩放)。提供了Restfull接口上传,访问文档。底层存储功能支持:
- 本地存储
- 阿里云OSS
- FastDFS
添加依赖
<dependency>
<groupId>com.rick.fileupload</groupId>
<artifactId>sharp-fileupload</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
配置存储策略
本地存储
默认就是本地存储。
application.xml
fileupload:
tmp: /Users/rick/jkxyx205/tmp/fastdfs/tmp # 下载的临时目录
local:
server-url: http://localhost:7892/ # 映射到tmp目录
root-path: /Users/rick/jkxyx205/tmp/fileupload
文件存储的路径是 root-path
。在本地开启一个静态服务器指向存储地址。访问url是 server-url
。
OSS
创建 Bucket
name为「sharp-fileupload」
application.xml
fileupload:
tmp: /Users/rick/jkxyx205/tmp/fastdfs/tmp # 下载的临时目录
oss:
endpoint: oss-cn-beijing.aliyuncs.com
accessKeyId: xxx
accessKeySecret: xxx
bucketName: sharp-fileupload
添加依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
配置类
@Bean
public InputStreamStore ossInputStreamStore(OSSProperties ossProperties) {
OSS ossClient = new OSSClientBuilder().build(ossProperties.getEndpoint(), ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret());
return new OSSInputStreamStore(ossClient, ossProperties);
}
FastDFS
fdfs_client.properties
fastdfs.tracker_servers=192.168.0.117:22122
fastdfs.http_tracker_http_port=8080
添加依赖
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27-SNAPSHOT</version>
</dependency>
配置类
@Bean
public InputStreamStore fastDFSInputStreamStore() throws IOException, MyException {
return new FastDFSInputStreamStore("fdfs_client.properties");
}
测试InputStreamStore
接口信息:
public interface InputStreamStore {
StoreResponse store(String groupName, String extension, InputStream is) throws IOException;
/**
*
* @param groupName
* @param storeName 磁盘存储的文件名
* @param extension 扩展名
* @param is
* @return
* @throws IOException
*/
StoreResponse store(String groupName, String storeName, String extension, InputStream is) throws IOException;
/**
* 删除文件
* @param groupName
* @param path
* @return
*/
void delete(String groupName, String path) throws IOException;
/**
* 获取访问地址
* @param groupName
* @param path
* @return
*/
String getURL(String groupName, String path);
/**
* 获取文件流
* @param groupName
* @param path
* @return
* @throws IOException
*/
InputStream getInputStream(String groupName, String path) throws IOException;
/**
* 获取字节数据
* @param groupName
* @param path
* @return
* @throws IOException
*/
byte[] getByteArray(String groupName, String path) throws IOException;
}
测试代码:
@Autowired
private InputStreamStore inputStreamStore;
private static String path;
@Test
@Order(1)
public void testPropertyStore() throws IOException {
StoreResponse response = inputStreamStore.store("group", "jpeg",
new FileInputStream("/Users/rick/jkxyx205/tmp/fileupload/demo/1.jpg"));
System.out.println(response.getGroupName());
System.out.println(response.getPath());
System.out.println(response.getFullPath());
System.out.println(response.getUrl());
path = response.getPath();
}
@Test
@Order(2)
public void getURL() {
String url = inputStreamStore.getURL("group", path);
System.out.println(url);
}
@Test
@Order(3)
public void getInputStream() throws IOException {
InputStream is = inputStreamStore.getInputStream("group", path);
FileUtils.copyInputStreamToFile(is, new File("/Users/rick/jkxyx205/tmp/fileupload/download/1.png"));
is.close();
}
@Test
@Order(Order.DEFAULT)
public void testPropertyDelete() throws IOException {
inputStreamStore.delete("group", path);
}