​在线问题反馈模块实战(九)​:实现图片上传功能(下)

举报
bug菌 发表于 2023/05/11 23:31:00 2023/05/11
【摘要】 我是bug菌,一名想走👣出大山改变命运的程序猿。接下来的路还很长,都等待着我们去突破、去挑战。来吧,小伙伴们,我们一起加油!未来皆可期,fighting!

👨‍🎓作者:bug菌
✏️博客:CSDN掘金infoQ51CTO
🎉简介:CSDN/阿里云/华为云/51CTO 博客专家,博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者,全网粉丝合计10w+,硬核微信公众号猿圈奇妙屋」,欢迎你的加入!免费领取简历模板/学习资料/大厂面试真题/职业规划等海量资料。
..
✍️温馨提醒:本文字数:1999字, 阅读完需:约 6 分钟

        嗨,家人们,我是bug菌呀,我又来啦。今天我们来聊点什么咧,OK,接着为大家更《springboot零基础入门教学》系列文章吧。希望能帮助更多的初学者们快速入门!

        如果小伙伴们在批阅文章的过程中觉得文章对自己有帮助,请别吝啬手中的赞呀,大胆的把文章点亮👍,相信你点赞了好的文章,平台也会经常给你推荐高质量好文,您的点赞三连(收藏+关注+留言)就是对bug菌写文道路上最好的鼓励与支持😘。时光不弃🏃🏻‍♀️,创作不停💕,加油☘️

一、前言🔥

       接下来的这几期,bug菌想跟大家分享一下自己昨天刚接到一个临时的需求,热乎着呢,想分享一下自己是如何面对临时需求并制定整个开发周期,其中包括从梳理业务到创建业务表再到实现业务逻辑形成闭环再到与前端对接,其中会穿插一些业务拓展及功能性拓展,这一条龙流程在线与大家一起见证,分享给刚入门的小伙伴,希望对你们有所帮助。

环境说明:idea2019.3 + springboot2.3.1.REALSE + mybati-plus3.2.0 + mysql5.6 + jdk1.8

       若小伙伴们在批阅文章的过程中觉得文章对您有一丝丝帮助,还请别吝啬您手里的赞呀,大胆的把文章点亮👍吧,您的点赞三连(收藏⭐️+关注👨‍🎓+留言📃)就是对bug菌我创作道路上最好的鼓励与支持😘。时光不弃🏃🏻‍♀️,创作不停💕,加油☘️ 

二、正文🔥

         上一期我们讲述了如何从一个业务梳理成具体的某些接口,再到接口如何定义,需要注意那些事项,不知道大家是否有记起来,然而这一期,我们就顺着已经确定的接口上实现业务逻辑,可能你们还会问,不就是对于该接口进行一个代码实现么,是的没错,但是我此处讲的肯定是经过了自己改了又改,删了又写最终得出来的,我就要拿最优的代码思路给大家做参考,但是也并不是说我这就是最优的,肯定都是有值得优化的地方,这里就需要发挥大家的思维与创造力了。

        好啦,咱们进入正题吧。手把手带着大家进行coding。

三、接口逻辑实战🔥

        我们先是在Controller层定义了该问题反馈保存接口及访问接口路径等,具体定义如下:

    /**
     * 反馈问题保存
     *
     * @param images          img图片数组
     * @param inPage          问题反馈/建议所在页面
     * @param questionContent 反问题反馈/建议描述
     * @param questionType    问题反馈类型(功能异常、体验问题、功能建议、其它)
     */
    @PostMapping("/save")
    @SysLog(logType = LogTypeEnum.LOG_TYPE_IMG_UPLOAD)
    @ApiOperation(value = "反馈问题保存", notes = "反馈问题保存")
    public ResultResponse<Boolean> saveQuestion(@ApiParam("图片数组") MultipartFile[] images,
                                                @ApiParam("问题反馈类型") String questionType,
                                                @ApiParam("问题反馈/建议描述") String questionContent,
                                                @ApiParam("问题反馈/建议所在页面") String inPage) throws IOException {
        return userQuestionsService.saveQuestion(images, questionType, questionContent, inPage);
    }

        接着就是实现saveQuestion()方法了。这里对于肯定是直接先定义到userQuestionsService接口层,然后通过实现该接口层进行方法的具体实现。

定义接口方法如下:

ResultResponse<Boolean> saveQuestion(MultipartFile[] images, String questionType, String questionContent, String inPage) throws IOException;

        这里我是直接反回了一个自定义类型,且自定义类型是个布尔值。

        然后就是该接口的的重点了,怎么说呢?这也是该接口的核心业务了,如何保存反馈内容,如何保存问题反馈图片数组。切记这里是支持多图片上传。

        接着就是通过UserQuestionsServiceImpl 通过implements关键字来实现 IUserQuestionsService 接口层的所有方法,其中也就包括实现saveQuestion()方法。

@Override
public ResultResponse<Boolean> saveQuestion(MultipartFile[] images, String questionType, String questionContent, String inPage) throws IOException {}

        对于这个业务实现,也是有点坑的。对于业务实现,我们一般要做的步骤就是,第一步干嘛,第二步干嘛...一定要先列举清楚,要不然写一点敲一点,最后肯定是有问题的。

第一步:遍历MultipartFile[] images 图片数组,分别调用图片上传方法。

第二步:实现图片上传方法。

第三部:实现基础字段内容保存并方法返回。

        然后具体对于三步骤,我们也是要具体的业务思维,对于遍历图片保存,这里就需要有一点要注意,你应该要留意,我们一次问题反馈调用,就生成一条记录,但对于一次请求,对于多图片,我们怎么做到一对多呢?其实啊,这就在你定义表字段时,就应该考虑清楚,所以我这里就根据一个字段来处理,也就是在你进行保存图片之后,专属定义一个外循环变量来存放所有的图片保存地址,然后两图片地址之间,你可以用逗号隔开,也可以用别的字段间隔都可。

imgPaths = imgPaths + "," + saveImgPath;

        就像如上所示,直接用逗号隔开,但是有一点是需要大家注意的,在你进行逗号隔开时,你要考虑如果MultipartFile[] images 图片数组为1,你就不应该有逗号间隔了啊。

        讲到这里,我也诺列的差不多了,想必小伙伴们心里都清楚如何实现了吧?

第一步:遍历调用图片上传方法

if (images.length > 0) {

            //分批处理图片保存
            for (int i = 1; i <= images.length; i++) {

                //当前图片
                MultipartFile img = images[i - 1];

                //获取文件后缀。
                String imageSuffix = FilenameUtils.getExtension(img.getOriginalFilename()).toLowerCase();
                //非以上文件后缀直接跳过。
                if (!CONTENT_TYPES.contains(imageSuffix)) {
                    continue;
                }

                //获取当前时间进行命名。
                String template = TimeUtils.getPrivateTime(ConstantUtils.TIME_STAMP_BEFORE2) + "_" + i;

                //重写img文件名 eg:20220527100245.jpg
                String saveImgPath = template + "." + imageSuffix;

                //图片保存绝对地址
                String savePath = this.buildImgPath(saveImgPath, accountId);

                //图片上传
                uploader.uploadImg(img, savePath);

                //路径用逗号拼接。
                if (StringUtils.isNotEmpty(imgPaths)) {
                    imgPaths = imgPaths + "," + saveImgPath;
                } else {
                    imgPaths = saveImgPath;
                }
            }
        }

注意:这里会涉及到两个比较坑的点。第一个就是对于同一个遍历方法,获取到的时间戳会是一致的,所以如果你想自定义别名图片,你就不能只通过时间戳来进行别名,你应该还得加盐,由于我是想区分用户那天上传的那些图片,才特定通过获取时间戳来进行别名,我这里提供的做法就是在时间戳后再拼接一个遍历下边,这样就保证了同一用户在一次接口调用反馈时,图片是按template_index来命名的。第二点就是需要留意对于图片数组=1的,你就不应该也逗号间隔,加一个判断即可。

第二步:实现图片上传方法

        对于这一步,实现方式还是相对较多的,我这里为了给大家教学,就使用一种超级简单的实现方式,比如MultipartFile类自带的transferTo()方法。

    public void uploadImg(MultipartFile image, String savePath) throws IOException {

        File file = new File(savePath);
        //检测是否存在该目录
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }

        //写入文件
        image.transferTo(file);
    }

         这里需要注意的就是一点,要判断该路径存在与否,不存在还是得要进行路径的创建再进行文件写入。

第三步:实现实体类数据库写入方法

        这一步就相对比较简单了,我们由于将已上传成功返回的图片保存地址进行了路径拼接,我们即可直接定义一个实体,进行数据赋值即可。

UserQuestionsEntity entity = new UserQuestionsEntity(user, questionType, questionContent, inPage, imgPaths);

然后在对应的实体类将字段内容进行手动赋值即可。

    public UserQuestionsEntity(SysUserEntity user, String questionType, String questionContent, String inPage, String imgPath) {
        this.status = UsualStatusEnum.NORMAL.getKey();
        this.creatorAccountId = user.getAccountId();
        this.creatorName = user.getAccountName();
        this.creatorDeptName = user.getDeptName();
        this.questionType = questionType;
        this.questionContent = questionContent;
        this.inPage = inPage;
        this.filePaths = imgPath;
    }

 这里就不需要再手动赋值那些创建时间、创建人等操作字段了,因为我们前边是已经添加了字段自动填充。

        最后就是直接调用mp提供的save方法即可。

return new ResultResponse<>(this.save(entity));

第四步:接口调用测试  

        最后,我们重启项目,通过postman进行接口调用测试。


 我们来检查一下具体保存的图片。

        很完美也是成功将图片保存到了指定位置下。

... ...

        好啦,以上就是这期的所有内容啦,你们学废了么?如果对你有所帮助,还请不要忘记给bug菌[三连支持]哟。如果想获得更多的学习资源或者想和更多的技术爱好者一起交流,可以关注我的公众号『猿圈奇妙屋』,后台回复关键词领取学习资料、大厂面经、面试模板等海量资源,就等你来拿。

三、往期热文推荐🔥

        对于问题反馈模块实战开发,我完整的梳理了每一期的教学及链接地址,仅供参考:希望能对你们有所帮助。

        如上是整整二十期内容,每一期都是干货,对于一个模块的开发,如何一点一滴打造并测试部署上线,我再说一遍,这不是演习,是实战!是实战!是实战!

        若你们觉得只是需要了解其中某个知识点或者业务的话,也不反对,你就选择其中的几期进行学习就好,反正都已经完结啦;我只希望你们能有所收获,有所成长,也就不枉我苦心每天下班后给大家总结更新。

四、文末🔥

        如果还想要学习更多,小伙伴们直接订阅bug菌专门为大家创建的零基础入门Spring Boot专栏《滚雪球学Spring Boot》,从无到有,从零到一!以知识点+实例+项目的学习模式由浅入深对Spring Boot框架进行学习&使用。

       我是bug菌,一名想走👣出大山改变命运的程序猿。接下来的路还很长,都等待着我们去突破、去挑战。来吧,小伙伴们,我们一起加油!未来皆可期,fighting!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。