springboot+ftp服务器上传图片推荐方案

springboot处理上传图片推荐方案

❤️弱水三千,只取一瓢饮❤️
🤞你好啊,我是小酥肉,欢迎阅读本博客👌

阅读前提示:
本文的代码不能直接运行,是说明问题的核心代码,只是一个实例❤️

选择方案的根据

关于前后端交互图片等二进制数据,我们在方案选择上无非要注重的是下面几点:

  • 前端怎么传?
  • 后端怎么接收?
  • 接收要干什么?
  • 怎么保存?保存到哪?

所以本文来介绍springboot(java服务端)处理上传图片的推荐方案和具体的实现流程,

推荐方案

前端ajax+springboot的MultipartFile+关系数据库保存图片路径+上传图片到图片服务器

具体解释:
前端使用ajax核心技术,传输数据类型为from-data,通过http协议传输二进制数据(即图片)
后端在特定Restful 端点处用使用 io流将二进制数据进行接收,并封装为MultipartFile对象,并做业务处理(如图片压缩,重命名)
后端将二进制数据上传到图片服务器,本文使用vsftpd(sftp协议)作为传输端口,nginx/oss存储对象服务器作为图片服务器(也就是图片真实存放的位置)。

下面根据一个业务实战说明整个图片的上传流程

业务背景

现在有一个《上传商品》接口,该接口需要完成

  • 参数校验
  • 业务处理
  • 图片上传
  • 保存数据库(尤其是图片在图片服务器的url字段)

定义接收dto

我们首先定义一个ItemAddRequest,用于封装前端传来的参数

@Data
public class ItemAddRequest implements Serializable {

    private static final long serialVersionUID = -1895470701830743471L;
    /**
     * 商品名字
     */
    private String itemName;
    /**
     * 商品图片
     */
    private MultipartFile[] images;

}

这里的images就是接收的图片数组,允许上传多图片。

Postman测试图片数组

然后用Postman的body -> form-data格式来进行传输,选择images字段为file类型,然后选择本地的几张图片进行上传

注意:这里最好用json传,因为对于二进制图片的json数据,数据量很大,推荐就是content-type为multipart/form-data。
可以看我写的博客 form-data VS json

在这里插入图片描述
然后在controller的@ModelAttribute接收这个form-data类型的数据,并将其封装为ItemAddRequest进行处理:

@RestController
@RequestMapping("/user/item")
@Slf4j
public class ItemController {
    @PostMapping("/upload")
    @PreAuthorize("@Autho.hasAuthority('sys:user')")
    public ResponseResult<Long> addItem(@ModelAttribute ItemAddRequest itemAddRequest) {
        //请求参数是否为空
        //对参数业务逻辑校验
        checkItemService.validCheckItem(checkItem, images);
        //上传图片,保存数据库
        checkItemService.saveCheckItem(checkItem,images);
        //return sth.....
    }
}

注意:这里要注意@ModelAttribute和@RequestBody
使用@ModelAttribute注解的实体类接收前端发来的数据格式需要为"x-www-form-urlencoded",@RequestBody注解的实体类接收前端的数据格式为JSON(application/json)格式。(若是使用@ModelAttribute接收application/json格式,虽然不会报错,但是值并不会自动填入)

参数校验

对dto参数进行校验

这里主要说明对图片的检验:验证图片大小,格式,是否符合要求的图片(我这里用到了训练模型视觉识别商品)

    private static final List<String> ALLOWED_EXTENSIONS = Arrays.asList("jpg", "jpeg", "png", "gif","webp");

 public static void validImage(MultipartFile file) {
        // 验证文件是否为空
        if (file == null|| file.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"图片不能为空");
        }
        if (file.getSize() >= 20 * 1024 * 1024) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"图片大小超出最大限制");
        }
        //验证文件格式符合要求(jpg,jpeg,png,gif)
        String originalFilename = file.getOriginalFilename();
        if (originalFilename != null && !originalFilename.isEmpty()) {
            String extension = getFileExtension(originalFilename);
            if (!ALLOWED_EXTENSIONS.contains(extension.toLowerCase())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"图片格式不正确");
            }
        }
        // 验证文件内容是否为有效的图片
        BufferedImage bufferedImage = null;
        try {
            bufferedImage = ImageIO.read(file.getInputStream());
        } catch (IOException e) {
            throw new fileException(ErrorCode.OPERATION_ERROR,"ImageIo读取图片失败");
        }
        if(bufferedImage==null){
            throw new fileException(ErrorCode.OPERATION_ERROR,"ImageIo读取图片失败");
        }
        // py视觉校验该图片
        if(!isPyReviewOk()){
            throw new fileException(ErrorCode.OPERATION_ERROR,"图片识别失败");

        }

    }

然后在检测商品的封装中加入检测图片的封装

    public void validCheckItem(CheckItem checkItem, MultipartFile[] itemImages) {
		//对checkItem检验 ....
        //图片检测 循环
        for(MultipartFile itemImage:itemImages){
            ImageUtils.validImage(itemImage);
        }
    }

图片上传

图片上传中,我们要将MultipartFile对象转为file对象,然后将file对象通过ftp服务,将file写入到图片服务器中

这里我使用的图片服务器是nginx,ftp服务用的是vsftpd
关于nginx充当图片服务器的博客推荐:nginx搭建图片服务器
关于vsftpd的博客推荐:一文通关vsftpd
这里我使用的java处理ftp的依赖是jsch:关于其他的依赖请参考
java用ftp做数据传输方案

下面是核心代码(参杂了部分业务逻辑,如将图片重命名并转换文件,ftp上传图片):

public boolean saveCheckItem(CheckItem checkItem,MultipartFile[] itemImages) {
        ArrayList<String> names = new ArrayList<>();
        for(MultipartFile itemImage:itemImages){
            //重命名文件
            String newImageName = ImageUtils.generateFileName(itemImage.getOriginalFilename(), checkItem.getUserId());
            names.add(newImageName);
            //转换文件
            File imageFile = FtpUtils.convertMultipartFileToFile(itemImage, newImageName);
            //上传文件
            FtpUtils.SftpUploadToServer(imageFile);
        }
        //保存数据库
        checkItem.setItemImage("http://47.106.211.119:777/images/"+String.join("\\", names));
        checkItem.setUserId(checkItem.getUserId());
        return this.save(checkItem);
    }

下面是ftp工具的SftpUploadToServer实现(最基础的):

public static void SftpUploadToServer(File file) {
		//创建一个JSch对象,
        JSch jsch = new JSch();
        Session session = null;
        try {
        	//获取一个session,使用test用户传输
            session = jsch.getSession("test", "47.106.211.119", 22);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setPassword("xsr2004217");
            session.connect();
			//使用Channel connect
            ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();
			//进入工作目录(这将是图片保存的位置)
            channelSftp.cd("/ershou/img_nginx/nginx/html/images");
            log.info("成功连接sftp服务器,cd到工作目录");
            //写入图片
            FileInputStream inputStream = new FileInputStream(file);
            channelSftp.put(inputStream, file.getName());
            log.info("传输成功!");
            inputStream.close();
            channelSftp.disconnect();
            //因为io流的原理 这里会遗留一个副本到本地,同时删除本地图片
            if (file.delete()) {
                log.info("删除本地照片副本成功!");
            } else {
                log.info("失败啦!",new RuntimeException("删除文件失败!"));
            }
        } catch (JSchException | SftpException | IOException e) {
            throw new RuntimeException(e);
        } finally {
        //关闭资源
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        }
    }
  • 这种实现是最常见,最朴素**的图片交互方案
  • 我们的实现逻辑是:前端ajax+springboot的MultipartFile+关系数据库保存图片路径+上传图片到图片服务器
  • 具体到后端:我们做了参数接收,参数校验,业务处理,保存数据库,上传图片等。

总结

通过阅读本博客您将收获下面知识点:

  • springboot处理图片推荐方案
  • ftp在java的使用
  • form-data VS json
  • @ModelAttribute和@RequestBody注解区别

❤️弱水三千,只取一瓢饮❤️
🤞我是小酥肉 ,喜欢简单 ,期待您的留言👌
上期文章:RabbitMQ消息队列(二):业务实战订单超时处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程就是n踢r

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值