智能协图云图库学习笔记day5

由于前面部分内容极具模板化通用性,因此单独抽取出来作为具体知识点模块笔记学习。详情在《JAVA后端必会模板》专栏中。由于项目开发文档已经足够详细,因此这里只记录要点。

支持用户上传图片和审核功能

方案设计

逻辑流程

上传:开放上传权限

审核:通过增加审核字段,由管理员身份审核。用户只能看见审核通过的图片。

库表设计

java 复制代码
ALTER TABLE picture  
    
    ADD COLUMN reviewStatus INT DEFAULT 0 NOT NULL COMMENT '审核状态:0-待审核; 1-通过; 2-拒绝',  
    ADD COLUMN reviewMessage VARCHAR(512) NULL COMMENT '审核信息',  
    ADD COLUMN reviewerId BIGINT NULL COMMENT '审核人 ID',  
    ADD COLUMN reviewTime DATETIME NULL COMMENT '审核时间';  
  

CREATE INDEX idx_reviewStatus ON picture (reviewStatus);

后端开发

数据模型开发

实体类 Picture 新增

java 复制代码
  
private Integer reviewStatus;  
  
  
private String reviewMessage;  
  
  
private Long reviewerId;  
  
  
private Date reviewTime;

图片查询请求类 PictureQueryRequest 新增

java 复制代码
  
private Integer reviewStatus;  
  
  
private String reviewMessage;  
  
  
private Long reviewerId;

新建审核状态枚举类

java 复制代码
@Getter  
public enum PictureReviewStatusEnum {  
    REVIEWING("待审核", 0),  
    PASS("通过", 1),  
    REJECT("拒绝", 2);  
  
    private final String text;  
    private final int value;  
  
    PictureReviewStatusEnum(String text, int value) {  
        this.text = text;  
        this.value = value;  
    }  
  
      
    public static PictureReviewStatusEnum getEnumByValue(Integer value) {  
        if (ObjUtil.isEmpty(value)) {  
            return null;  
        }  
        for (PictureReviewStatusEnum pictureReviewStatusEnum : PictureReviewStatusEnum.values()) {  
            if (pictureReviewStatusEnum.value == value) {  
                return pictureReviewStatusEnum;  
            }  
        }  
        return null;  
    }  
}
管理员审核功能

开发请求包装类

开发审核服务

实现类

开发审核接口

审核状态设置

权限控制

设置审核状态

图片上传、用户编辑、管理员更新这 3 个操作都需要设置审核状态,所以我们可以先编写一个通用的 "补充审核参数" 的方法,根据用户的角色给图片对象填充审核字段的值。

java 复制代码
@Override  
public void fillReviewParams(Picture picture, User loginUser) {  
    if (userService.isAdmin(loginUser)) {  
        
        picture.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());  
        picture.setReviewerId(loginUser.getId());  
        picture.setReviewMessage("管理员自动过审");  
        picture.setReviewTime(new Date());  
    } else {  
        
        picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getValue());  
    }  
}
控制内容可见性

需要修改主页调用的 listPictureVOByPage 接口,补充查询条件即可,默认只能查看已过审的数据

同步更改 PictureService 的 getQueryWrapper 方法,支持根据审核字段进行查询

通过 URL 导入图片

方案设计

1)下载图片:后端服务器从指定的远程 URL 下载图片到本地临时存储。对于 Java 项目,可以直接使用 Hutool 的 HttpUtil.downloadFile 方法一行代码完成。

2)校验图片:跟验证本地文件一样,需要校验图片的格式、大小等。也可以直接校验 URL 字符串本身的合法性。

3)上传图片:将校验通过的图片上传到对象存储服务,生成存储 URL。

后端开发

服务开发
java 复制代码
public UploadPictureResult uploadPictureByUrl(String fileUrl, String uploadPathPrefix) {  
    
    
    validPicture(fileUrl);  
    
    String uuid = RandomUtil.randomString(16);  
    
    String originFilename = FileUtil.mainName(fileUrl);  
    String uploadFilename = String.format("%s_%s.%s", DateUtil.formatDate(new Date()), uuid,  
            FileUtil.getSuffix(originFilename));  
    String uploadPath = String.format("/%s/%s", uploadPathPrefix, uploadFilename);  
    File file = null;  
    try {  
        
        file = File.createTempFile(uploadPath, null);  
        
        HttpUtil.downloadFile(fileUrl, file);  
        
        
    } catch (Exception e) {  
        log.error("图片上传到对象存储失败", e);  
        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");  
    } finally {  
        this.deleteTempFile(file);  
    }  
}

要点:

通过URL获取名称:FileUtil.mainName(fileUrl)

通过URL下载文件:HttpUtil.downloadFile(fileUrl,file)

校验 URL 图片
java 复制代码
private void validPicture(String fileUrl) {  
    ThrowUtils.throwIf(StrUtil.isBlank(fileUrl), ErrorCode.PARAMS_ERROR, "文件地址不能为空");  
  
    try {  
        
        new URL(fileUrl); 
    } catch (MalformedURLException e) {  
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件地址格式不正确");  
    }  
  
    
    ThrowUtils.throwIf(!(fileUrl.startsWith("http://") || fileUrl.startsWith("https://")),  
            ErrorCode.PARAMS_ERROR, "仅支持 HTTP 或 HTTPS 协议的文件地址");  
  
    
    HttpResponse response = null;  
    try {  
        response = HttpUtil.createRequest(Method.HEAD, fileUrl).execute();  
        
        if (response.getStatus() != HttpStatus.HTTP_OK) {  
            return;  
        }  
        
        String contentType = response.header("Content-Type");  
        if (StrUtil.isNotBlank(contentType)) {  
            
            final List<String> ALLOW_CONTENT_TYPES = Arrays.asList("image/jpeg", "image/jpg", "image/png", "image/webp");  
            ThrowUtils.throwIf(!ALLOW_CONTENT_TYPES.contains(contentType.toLowerCase()),  
                    ErrorCode.PARAMS_ERROR, "文件类型错误");  
        }  
        
        String contentLengthStr = response.header("Content-Length");  
        if (StrUtil.isNotBlank(contentLengthStr)) {  
            try {  
                long contentLength = Long.parseLong(contentLengthStr);  
                final long TWO_MB = 2 * 1024 * 1024L; 
                ThrowUtils.throwIf(contentLength > TWO_MB, ErrorCode.PARAMS_ERROR, "文件大小不能超过 2M");  
            } catch (NumberFormatException e) {  
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件大小格式错误");  
            }  
        }  
    } finally {  
        if (response != null) {  
            response.close();  
        }  
    }  
}

要点:

1.利用Head只获取Http请求头信息进行校验response=HttpUtil.createRequest(Method.HEAD,fileUrl).execute();

2.校验文件类型 String contentType=response.header("Content-Type");

3.校验文件大小 String contentLengthStr =response.header("Content-Length"),Long.parseLong()

4.关闭响应资源 response.close() 如io流,网络资源,数据库连接等操作系统级资源都需要手动关闭

优化代码 - 模板方法模式

模板方法模式是行为型设计模式,适用于具有通用处理流程、但处理细节不同的情况。通过定义一个抽象模板类,提供通用的业务流程处理逻辑,并将不同的部分定义为抽象方法,由子类具体实现。

要点:

复制代码
1.@Deprecated  表示该类废弃

2.使用Object inputSource 来兼容MultiPartFile与String

批量抓取和创建图片

方案设计

1、如何抓取图片?

在哪抓

bing图片库中抓取

如何抓

1.请求完整页面再解析HTML结构,获取图片下载地址URL

2.通过后端获取图片地址接口来获取图片。f12网络请求控制台里找。

使用jsoup解析HTML元素

2、抓取和导入规则

1.关键词2.抓取数量

后端开发

定义请求体

开发服务

引jsoup依赖

编写抓取方法

java 复制代码
@Override  
public int uploadPictureByBatch(PictureUploadByBatchRequest pictureUploadByBatchRequest, User loginUser) {  
    String searchText = pictureUploadByBatchRequest.getSearchText();  
    
    Integer count = pictureUploadByBatchRequest.getCount();  
    ThrowUtils.throwIf(count > 30, ErrorCode.PARAMS_ERROR, "最多 30 条");  
    
    String fetchUrl = String.format("https://cn.bing.com/images/async?q=%s&mmasync=1", searchText);  
    Document document;  
    try {  
        document = Jsoup.connect(fetchUrl).get();  
    } catch (IOException e) {  
        log.error("获取页面失败", e);  
        throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取页面失败");  
    }  
    Element div = document.getElementsByClass("dgControl").first();  
    if (ObjUtil.isNull(div)) {  
        throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取元素失败");  
    }  
    Elements imgElementList = div.select("img.mimg");  
    int uploadCount = 0;  
    for (Element imgElement : imgElementList) {  
        String fileUrl = imgElement.attr("src");  
        if (StrUtil.isBlank(fileUrl)) {  
            log.info("当前链接为空,已跳过: {}", fileUrl);  
            continue;  
        }  
        
        int questionMarkIndex = fileUrl.indexOf("?");  
        if (questionMarkIndex > -1) {  
            fileUrl = fileUrl.substring(0, questionMarkIndex);  
        }  
        
        PictureUploadRequest pictureUploadRequest = new PictureUploadRequest();  
        try {  
            PictureVO pictureVO = this.uploadPicture(fileUrl, pictureUploadRequest, loginUser);  
            log.info("图片上传成功, id = {}", pictureVO.getId());  
            uploadCount++;  
        } catch (Exception e) {  
            log.error("图片上传失败", e);  
            continue;  
        }  
        if (uploadCount >= count) {  
            break;  
        }  
    }  
    return uploadCount;  
}

要点:

1.拼接请求地址String.format()

2.通过核心类Document获取HTTP文档对象Jsoup.connect(fetchUrl).get()

3.定位图片列表的核心容器元素Element div=document.getElementsByClass("dgControl").first

4.提取所有图片元素 Elements imgElementList = div.select("img.mimg");

5.遍历挨个上传,清晰图片链接fileUrl.indexOf("?"),fileUrl.substring() ?后面参数可能携带非法字符等

扩展功能 - 批量设置属性
相关推荐
2501_933513042 小时前
Java后端开发者的AGI时代学习与职业路径策略
java·学习·agi
救救孩子把2 小时前
60-机器学习与大模型开发数学教程-5-7 学习率调度(warmup、余弦退火、OneCycle)
人工智能·学习·机器学习
lixin5565562 小时前
基于迁移学习的图像分类增强器
java·人工智能·pytorch·python·深度学习·语言模型
Hammer_Hans2 小时前
DFT笔记23
笔记
计算机学姐2 小时前
基于SpringBoot的校园跑腿系统【数据可视化统计+原创精品】
java·vue.js·spring boot·后端·mysql·信息可视化·echarts
va学弟2 小时前
Java 网络通信编程(1):服务器多任务连接+广播消息实现
java·运维·服务器
爱奥尼欧2 小时前
【通信原理】信道——信道、传输特性、多径效应、噪声、带宽、信道容量(笔记总结)
笔记
独自破碎E3 小时前
【双指针+字符串】字符串变形
android·java
weixin_462446234 小时前
一键安装 Hadoop 3.3.6 自动化脚本详解 |(含 JAVA_HOME 自动配置)
java·hadoop·自动化