Spring Boot电商项目35:商品模块二:【增加商品】接口之图片上传:【上传图片】接口开发;(主要是:Spring项目中,上传文件的步骤、套路和需要注意的问题)

 说明:

(1)为什么写本篇博客?:【我们知道,在新增商品的时候,是需要上传图片的】→【对应到接口上的表现就是:在开发【增加商品】接口的时候,我们需要传递一个image参数】→【这个image参数,就是图片在服务器上的地址啦】→【So,这个image地址是什么、从哪儿来?】→【由此,就涉及到【上传图片】接口了】→【我们在调用【增加商品】接口的时候,其会先调用【上传图片】接口,把图片先上传到服务器上,,,然后服务器会返回一个(经过安全处理的)该图片在服务器的地址】→【然后,这个返回的image地址,就会作为【增加图片】接口的image参数】;

所以,在开发【增加商品】接口的时候,必须要开发图片上传的功能,也就是必须要开发【上传图片】功能;

(2)本篇博客介绍的上传文件技术,其实具有很强的通用性;;;;对于以后遇到其他需要上传文件的开发场景时候,完全可以参考本篇博客的内容;

(3)关于文件上传:
          ● 以前在【后台系统四:【新增】功能;(FileUpload组件)】中,介绍过使用FileUpload组件实现上传文件;(PS:当时的那个项目是个,纯纯的Servlet和JSP项目);

          ● 但本篇博客,没有使用FileUpload组件;而是使用Spring提供的【@RequestParam("file") MultipartFile file】的方式,来实现文件的接收;

(4)服务器在保存静态资源的时候,出于安全的考虑;返回给前端的图片地址,并不是图片在服务器上的真实地址;关于这一点,在【Spring Boot电商项目36:商品模块二:【增加商品】接口之图片上传:资源映射开发;】作了详细介绍;

(5)声明:一个尚未仔细研究的点:MultipartFile与File的具体内容;

          ● 似乎可以参考【MultipartFile与File的一些事】;这篇博客自己还没看;

          ● 以后遇到了个性化的文件上传需求;或者遇到断点续传等特殊需求时;或者等到有精力的时候;可以再来仔细研究一下相关内容;

目录

一:【上传图片】接口说明;

二:开发【上传图片】接口:在ProductAdminController中,创建上传图片的方法:upload()方法;

(1)url,请求方式,要符合接口的要求;

(2)方法参数,为什么要引入HttpServletRequest?

(3)使用MultipartFile来实现文件的上传;(PS:这儿介绍的很浅)

(4)代码逻辑说明:使用【MultipartFile.getOriginalFilename();】去获取原始文件名;使用【UUID.randomUUID();】创建文件名;

(5)创建目录File对象,创建文件夹File对象;把文件按照指定的文件名,写入到服务器的指定目录中去;

(6)【配置文件中,配置自定义属性】并【使用@Value注解去获取属性,以赋值给变量】;

(7)【配置文件中,配置自定义属性】并【使用@Value注解去获取属性,以赋值给变量】:两个坑;

(8)上传文件时,如果出了问题:我们使用try-catch的方法把异常给捕获,而不是throw抛出;(PS:对这一点的理解,还不是太深……)

(9)至此,文件就上传到服务器了;;;下面就是(也可以说是根据接口要求,其实接口只能这么要求),返回(经过安全处理的)图片路径;

(10)这儿编写了一个通过【"http://127.0.0.1:8083/images/bfe5d66d-98b1-4825-9a86-de8c0741328a.png"】得到【"http://127.0.0.1:8083/"】的方法,以后需要的时候,直接过来copy就行了;;;其实这儿的核心就是这几个方法:uri.getScheme(), uri.getUserInfo(), uri.getHost(),uri.getPort();

三:测试接口; 

附加说明:上传图片大小的问题;

四:剩余问题说明:引出后面的【自定义静态资源映射】;


一:【上传图片】接口说明;

说明:

(1)这个接口返回的地址,不是图片上传到服务器后的真实地址,这是我们自定义的非真实地址;这主要是出于安全考虑;

(2)虽然这个接口的url中有admin,但是只访问这个接口时,可以不是管理员用户登录的状态;而且,我们在【Spring Boot电商项目27:商品分类模块六:统一校验管理员身份;(选用【J2EE中的过滤器】来实现需求;重难点是【如何在Spring Boot项目中,使用过滤器】;)】中配置的时候,也没有配置【/admin/upload/file】这个地址;

但及时如此,在项目上线后,用户是无法单独调用【上传图片】接口的;;;而是在调用【增加图片】接口的时候,顺带调用【上传图片】接口;


二:开发【上传图片】接口:在ProductAdminController中,创建上传图片的方法:upload()方法;

    /**
     * 上传文件(这儿具体来说,就是图片)
     * @param httpServletRequest
     * @param file
     * @return
     */
    @ApiOperation("上传文件(这儿具体来说,就是图片)")
    @PostMapping("/admin/upload/file")
    @ResponseBody
    public ApiRestResponse upload(HttpServletRequest httpServletRequest, @RequestParam("file") MultipartFile file) {

        //获取文件的原始名字
        String fileName = file.getOriginalFilename();
        //通过截取最后一个“.”后面的内容,获取文件扩展名
        String suffix = fileName.substring(fileName.lastIndexOf("."));

        //利用UUID,生成文件上传到服务器中的文件名;
        UUID uuid = UUID.randomUUID();//通过Java提供的UUID工具类,获取一个UUID;
        //把uuid和文件扩展名,拼凑成新的文件名;
        String newFileName = uuid.toString() + suffix;

        //生成文件夹的File对象;
        File fileDirectory = new File(Constant.FILE_UPLOAD_DIR);
        //生成文件的File对象;
        File destFile = new File(Constant.FILE_UPLOAD_DIR + newFileName);
        //如果文件夹不存在的话
        if (!fileDirectory.exists()) {
            //如果在创建这个文件夹时,创建失败,就抛出文件夹创建失败异常
            if (!fileDirectory.mkdir()) {
                throw new ImoocMallException(ImoocMallExceptionEnum.MKDIR_FAILED);
            }
        }
        //如果能执行到这儿,说明文件夹已经创建成功了;;;那么就把传过来的文件,写入到我们指定的File对象指定的位置中去;
        try {
            file.transferTo(destFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //执行到这儿以后,表示,我们已经把文件,存放到指定的位置了;
        //接下来,就是组织图片的url,返回给前端;
        try {
//            System.out.println(httpServletRequest.getRequestURL() + "");
//            System.out.println(getHost(new URI(httpServletRequest.getRequestURL() + "")));
            return ApiRestResponse.success(
                    getHost(new URI(httpServletRequest.getRequestURL() + "")) +
                            "/images/" + newFileName);
        } catch (URISyntaxException e) {
            //如果上面的过程出现了问题,就抛出文件上传失败异常;
            return ApiRestResponse.error(ImoocMallExceptionEnum.UPLOAD_FAILED);
        }
    }

    /**
     * 工具方法:获取图片完整地址中的,URI:
     * 即通过【"http://127.0.0.1:8083/images/bfe5d66d-98b1-4825-9a86-de8c0741328a.png"】得到【"http://127.0.0.1:8083/"】
     * @param uri
     * @return
     */
    private URI getHost(URI uri) {
        URI effectiveURI;
        try {
            //
            effectiveURI = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(),
                    uri.getPort(), null, null, null);
        } catch (URISyntaxException e) {
            effectiveURI = null;
        }
        return effectiveURI;
    }

说明:

(1)url,请求方式,要符合接口的要求;

……………………………………………………

(2)方法参数,为什么要引入HttpServletRequest?

PS:

          ● 上面的getHost()方法,是我们自己编写的;

          ● 有关getRequestURL的内容,可以参考【getContextPath、getServletPath、getRequestURI、getRequestURL、getRealPath、getPathInfo的区别;URI和URL的区别比较;】;

……………………………………………………

(3)使用MultipartFile来实现文件的上传;(PS:这儿介绍的很浅)

MultipartFile包含文件的二进制数据和原始文件名;

PS:几点说明:

          ● 在【后台系统四:【新增】功能;(FileUpload组件)】中,也介绍了【enctype="multipart/form-data";】的内容;虽然与本篇博客的内容看起来存在差异;;;但其实,能够感受到,其背后的原理应该是一样的;

          ● 有关MultipartFile的相对详细的介绍,可以参考【MultipartFile简介;待写……】 

……………………………………………………

(4)代码逻辑说明:使用【MultipartFile.getOriginalFilename();】去获取原始文件名;使用【UUID.randomUUID();】创建文件名;

……………………………………………………

(5)创建目录File对象,创建文件夹File对象;把文件按照指定的文件名,写入到服务器的指定目录中去;

 ……………………………………………………

(6)【配置文件中,配置自定义属性】并【使用@Value注解去获取属性,以赋值给变量】;

……………………………………………………

(7)【配置文件中,配置自定义属性】并【使用@Value注解去获取属性,以赋值给变量】:两个坑;

          ● 第一个坑:在【Spring Boot入门七:【配置文件中,配置自定义属性】并【使用@Value注解去获取属性,以赋值给变量】;】中已经介绍过了:静态属性,添加一个非静态的setter方法,并在该setter方法上使用@Value注解;

          ● 第二个坑:这个坑非常重要,自己也知道对应的原理,但就是十分容易忽略:要想使得@Value注解生效,其所在的类需要被IoC容器管理起来,只有这个类被IoC容器管理起来了,Spring才会帮助我们去实际执行@Value,完成值的注入;

……………………………………………………

(8)上传文件时,如果出了问题:我们使用try-catch的方法把异常给捕获,而不是throw抛出;(PS:对这一点的理解,还不是太深……)

关于这一点的解释,可以参考下:

……………………………………………………

(9)至此,文件就上传到服务器了;;;下面就是(也可以说是根据接口要求,其实接口只能这么要求),返回(经过安全处理的)图片路径;

其实在上面的【(2)方法参数,为什么要引入HttpServletRequest?】中,已经解释的差不多了;

那么,如果一切成功,【上传图片】接口,就会向前端返回这个路径,然后前端拿到这个路径后,就会作为【增加商品】接口的image属性,然后这个路径就会被保存在数据库中;

至于为什么不返回图片的真实地址,而是拼凑一个“错误”的地址,是因为:如果将真实路径反馈给前台,可能会暴露文件的地址,从而可能导致文件的泄露,所以一般返回给用户的都不是真实的路径。

……………………………………………………

(10)这儿编写了一个通过【"http://127.0.0.1:8083/images/bfe5d66d-98b1-4825-9a86-de8c0741328a.png"】得到【"http://127.0.0.1:8083/"】的方法,以后需要的时候,直接过来copy就行了;;;其实这儿的核心就是这几个方法:uri.getScheme(), uri.getUserInfo(), uri.getHost(),uri.getPort();


三:测试接口; 

启动项目;

附加说明:上传图片大小的问题;

默认情况下,图片大小不能超过1M,但是我们可以在application.properties配置文件中,配置以下内容,来设置;(经过实测,起码对于Spring Boot2.2.1这个版本来说,这是可以的)

spring.servlet.multipart.max-file-size=10MB

spring.servlet.multipart.max-request-size=10MB

四:剩余问题说明:引出后面的【自定义静态资源映射】;

PS:下图可能存在一个描述错误的地方:图片的真实地址:可能并不能说成是 【http://127.0.0.1:8083/E:/imooc-mall-upload-file/7b0fe9be-cdd6-4bd4-9119-dc1474c0ac05.jpg】;而应该说成是【http://127.0.0.1:8083/,这台服务器的本地的E:/imooc-mall-upload-file/7b0fe9be-cdd6-4bd4-9119-dc1474c0ac05.jpg这个位置】;(PS:关于,这其中的区别,自己并不是很清晰)

关于这一点,在【Spring Boot电商项目36:商品模块二:【增加商品】接口之图片上传:资源映射开发;】作了详细介绍;

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值