sharp-common集成验证

集成

注册ValidatorHelper.java

@Configuration
@RequiredArgsConstructor
public class AppConfig {

    private final Validator validator;

    @Bean
    public ValidatorHelper validatorHelper() {
        return new ValidatorHelper(validator);
    }
}

测试

User.java

@Value
@Builder
public class User {

    @PhoneValid
    private String phone;

    @NotBlank
    private String name;
}

Test.java

@Autowired
private ValidatorHelper validatorHelper;

@Test
public void testValidate() {
    User user = User.builder().name("rick").phone("18898987765").build();
    validatorHelper.validate(user);
}

@Test(expected = IllegalArgumentException.class)
public void testValidate2() {
    User user = User.builder().name("Rick").phone("188").build();
    validatorHelper.validate(user);
}

自定义验证

枚举值验证

枚举SexEnum UserStatusEnum

@AllArgsConstructor
@Getter
public enum SexEnum {
    FEMALE(0, "FEMALE"),
    MALE(1, "MALE");

    private static final Map<Integer, SexEnum> codeMap = new HashMap<>();

    static {
        for (SexEnum e: values()) {
            codeMap.put(e.code, e);
        }
    }

    private final Integer code;

    private final String label;

    public static SexEnum valueOfCode(Integer code) {
        return codeMap.get(code);
    }
}

@AllArgsConstructor
@Getter
public enum UserStatusEnum {
    NORMAL("NORMAL"),
    LOCKED("LOCKED");

    private final String label;

    public String getCode() {
        return this.name();
    }

    public static UserStatusEnum valueOfCode(String code) {
        return valueOf(code);
    }
}

枚举中必须包含静态方法 valueOfCode 和 方法 getCode

User 中添加属性

@Value
@Builder
public class User {

    @PhoneValid
    private String phone;

    @NotBlank
    private String name;

//    @EnumValid(target = SexEnum.class, message = "性别有误,不存在值${validatedValue}")
    @EnumValid(target = SexEnum.class)
    private int sex;

    @EnumValid(target = UserStatusEnum.class, message = "用户状态有误,不存在值${validatedValue}")
    private String userStatus;
}

测试:

@Test
public void testValidate3() throws BindException {
    User user = User.builder().name("rick").phone("18898987765").sex(1).userStatus("LOCKED").build();
    validatorHelper.validate(user);
}

@Test(expected = BindException.class)
public void testValidate4() throws BindException {
    User user = User.builder().name("rick").phone("18898987765").sex(3).userStatus("ENABLED").build();
    validatorHelper.validate(user);
}

sharp-fileupload文档转换(五)

PDF 转图片

  • pdf按页转多张图片
@Test
public void testPdf2Image1() throws IOException {
    File file = new File("/Users/rick/jkxyx205/tmp/fileupload/pdf/1.pdf");
    FileMeta fileMeta = FileMetaUtils.parse(file);

    List<byte[]> list = FileConvertUtils.pdf2Image(fileMeta.getData(), 150);
    File folder = new File("/Users/rick/jkxyx205/tmp/fileupload/pdf");
    for (int i = 0; i < list.size(); i++) {
        FileUtils.writeByteArrayToFile(new File(folder, i + ".png"), list.get(i));
    }
}
  • pdf转一张图片
@Test
public void testPdf2Image2() throws IOException {
    File file = new File("/Users/rick/jkxyx205/tmp/fileupload/pdf/1.pdf");
    FileMeta fileMeta = FileMetaUtils.parse(file);

    FileConvertUtils.pdf2Image(fileMeta.getData(),
            new FileOutputStream("/Users/rick/jkxyx205/tmp/fileupload/pdf/full.png"),
            150);
}

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 needs to be URL encoded, and the document must be publicly accessible on the internet.

微软接口文档

  • 后端处理
 /**
 * 预览文件: 通过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

http://xhope.top/wp-content/uploads/2021/10/475098507762896896.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()));
}