springboot实现文件上传和读取
作者:mmseoamin日期:2023-12-14

一.本地存储(将图片存放在本地的nginx服务器上)

  1. 在nginx上创建images目录,用来存储上传的文件

    (使用nginx的好处是暴露端口,便于外界访问,同时nginx作为静态资源服务器在处理静态资源方面更快)

    不然你访问文件就需要通过文件输入输出流进行输出

  2. 使用MultipartFile类提供的API方法,把临时文件转存到本地磁盘目录下

    MultipartFile 常见方法:

    • String getOriginalFilename(); //获取原始文件名
    • void transferTo(File dest); //将接收的文件转存到磁盘文件中
    • long getSize(); //获取文件的大小,单位:字节
    • byte[] getBytes(); //获取文件内容的字节数组
    • InputStream getInputStream(); //获取接收到的文件内容的输入流

下面来看一个案例:上传一个图片并返回图片存储的地址

注:前端上传文件要用表单参数,所以文档中的参数格式不再是applicant/json,multipart/form-data。而是后端接收表单参数与接收query参数一样,都用@Requestparam。

springboot实现文件上传和读取,image-20221227211742547,第1张

  • 基本信息

    请求路径:/upload
    请求方式:POST
    接口描述:上传图片接口
    
  • 请求参数

    参数格式:multipart/form-data

    参数说明:

    参数名称参数类型是否必须示例备注
    imagefile
  • 响应数据

    参数格式:application/json

    参数说明:

    参数名类型是否必须备注
    codenumber必须响应码,1 代表成功,0 代表失败
    msgstring非必须提示信息
    dataobject非必须返回的数据,上传图片的访问路径

    响应数据样例:

    {
        "code": 1,
        "msg": "success",
        "data": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-0400.jpg"
    }
    

    代码:

    @Slf4j
    @RestController
    public class UploadController {
        @PostMapping("/upload")
        public Result upload(String username, Integer age, MultipartFile image) throws IOException {
            log.info("文件上传:{},{},{}",username,age,image);
            //获取原始文件名
            String originalFilename = image.getOriginalFilename();
            //构建新的文件名
            String extname = originalFilename.substring(originalFilename.lastIndexOf("."));//文件扩展名
            String newFileName = UUID.randomUUID().toString()+extname;//随机名+文件扩展名
            //将文件存储在服务器的nginx目录下,这里是随便写的地址
            image.transferTo(new File("E:/images/"+newFileName));
            return Result.success();
        }
    }
    

    注:在SpringBoot中,文件上传时默认单个文件最大大小为1M,我们再次上传一个较大的文件(超出1M)时发现,后端程序报错。

    那么如果需要上传大文件,可以在application.properties进行如下配置:

    #配置单个文件最大上传大小
    spring.servlet.multipart.max-file-size=10MB
    #配置单个请求最大上传大小(一次请求可以上传多个文件)
    spring.servlet.multipart.max-request-size=100MB
    

    二.使用阿里云OSS

    1.什么是云服务

    云服务指的就是通过互联网对外提供的各种各样的服务,比如像:语音服务、短信服务、邮件服务、视频直播服务、文字识别服务、对象存储服务等等。

    当我们在项目开发时需要用到某个或某些服务,就不需要自己来开发了,可以直接使用阿里云提供好的这些现成服务接口就可以了。

    比如:在项目开发当中,我们要实现一个短信发送的功能,如果我们自己实现,会很繁琐,因为你需要和各个运营商进行对接。而此时阿里云完成了和三大运营商对接,并对外提供了一个短信服务。只需要调用阿里云提供的短信服务,就可以很方便的来发送短信了。

    简单说就是别人帮我们实现好了功能,我们只要调用接口即可。当然云服务提供商给我们提供的软件服务是需要收取一部分费用的。

    2.什么是阿里云OSS

    阿里云对象存储OSS(Object Storage Service),是一款海量、安全、低成本、高可靠的云存储服务。使用OSS,您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。

    在前端进行文件上传并请求到服务端时,在服务器本地磁盘当中就不需要再来存储文件了。我们直接将接收到的文件上传到oss,由 oss帮我们存储和管理。同时安全性还得到提高。

    3.怎么使用阿里云OSS

    无论使用什么样的云服务,阿里云也好,腾讯云、华为云也罢,在使用第三方的服务时,操作的思路都是一样的。

    springboot实现文件上传和读取,image-20221229093911113,第2张

    SDK:Software Development Kit 的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包)、代码示例等,都可以叫做SDK。

    简单说,sdk中包含了我们使用第三方云服务时所需要的依赖,以及一些示例代码。我们可以参照sdk所提供的示例代码就可以完成入门程序。

    接下来我们就来介绍一下我们当前要使用的阿里云oss对象存储服务具体的使用步骤。

    3.1 准备

    springboot实现文件上传和读取,image-20221229112451120,第3张

    Bucket:存储空间是用户用于存储对象(Object,就是文件)的容器,所有的对象都必须隶属于某个存储空间。

    下面我们根据之前介绍的使用步骤,完成准备工作:

    1. (1)打开https://www.aliyun.com/ ,申请阿里云账号并完成实名认证。

      springboot实现文件上传和读取,image-20221128012526497,第4张

      (2)充值 (可以不用做)

      (3)开通OSS

      登录阿里云官网。 点击右上角的控制台。

      springboot实现文件上传和读取,image-20221129214250389,第5张

      将鼠标移至产品,找到并单击对象存储OSS,打开OSS产品详情页面。在OSS产品详情页中的单击立即开通。

      springboot实现文件上传和读取,image-20221129214332892,第6张

      springboot实现文件上传和读取,image-20221129214403131,第7张

      springboot实现文件上传和读取,image-20221128012258544,第8张

      开通服务后,在OSS产品详情页面单击管理控制台直接进入OSS管理控制台界面。您也可以单击位于官网首页右上方菜单栏的控制台,进入阿里云管理控制台首页,然后单击左侧的对象存储OSS菜单进入OSS管理控制台界面。

      springboot实现文件上传和读取,image-20201126234535040,第9张

      (4)创建存储空间

      新建Bucket,命名为你想要的名字 ,读写权限为 公共读(这样外界才能读取图片)。

      springboot实现文件上传和读取,image-20221128014414947,第10张

    3.2 入门

    阿里云oss 对象存储服务的准备工作我们已经完成了,接下来我们就来完成第二步操作:参照官方所提供的sdk示例来编写入门程序。

    官方文档:https://help.aliyun.com/zh/oss/developer-reference/java-installation

    这里我们已经写好了阿里云操作相关的工具类。

    【1】引入依赖

    
        com.aliyun.oss
        aliyun-sdk-oss
        3.15.1
    
    

    【2】引入阿里云OSS上传文件工具类(由官方的示例代码改造而来)

    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.UUID;
    @Component
    public class AliOSSUtils {
        private String endpoint = "https://oss-cn-shanghai.aliyuncs.com";
        private String accessKeyId = "";
        private String accessKeySecret = "";
        private String bucketName = "web-framework01";
        /**
         * 实现上传图片到OSS
         */
        public String upload(MultipartFile multipartFile) throws IOException {
            // 获取上传的文件的输入流
            InputStream inputStream = multipartFile.getInputStream();
            // 避免文件覆盖
            String originalFilename = multipartFile.getOriginalFilename();
            String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
            //上传文件到 OSS
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            ossClient.putObject(bucketName, fileName, inputStream);
            //文件访问路径
            String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
            // 关闭ossClient
            ossClient.shutdown();
            return url;// 把上传到oss的路径返回
        }
    }
    

    上面变量的值需要根据自己的实际情况进行配置

    【1】endpoint是指你创建的bucket的地域节点,点击对象存储OSS,再点击Bucket列表,选择你的bucket,点击概览就可以在下面看到,注意复制外网访问的地域节点,因为我们存取都是在外网操作的。

    【2】bucketName就是要填写你的创造的存储空间bucket的名称。后面bucketName会接在endpoint前面,形成Bucket域名,通过访问域名存取文件。

    springboot实现文件上传和读取,YJHW_S2O%01R7$UW06PC4EO,第11张

    【3】accessKeyId和accessKeySecret就是你的密钥了。获取方式如下

    springboot实现文件上传和读取,image-20221128020105943,第12张

    3.3 集成使用

    现在我们就可以使用工具类来实现文件的存储。

    文件上传的基本逻辑就是将图片上传到阿里云OSS,然后在数据库中存储图片对应的image的url地址。

    而取图片的基本逻辑就是根据要取得图片的信息去得到对应的url,然后把url发送给前端。

    例子:实现学生请假功能,并且学生能查看之前他请假的详情

    (1)对应的开发文档

    POST /leaves

    学生请假

    Body 请求参数

    courseId: 课程id
    leavePlace: 去哪里
    appealBeginTime: 课程开始时间
    appealEndTime: 课程结束时间
    reason: 原因
    status: 状态
    leaveImage: string
    

    注:下面的参数除了Authorization的表头参数之外,其他都是表单参数

    springboot实现文件上传和读取,image1,第13张

    名称位置类型必选说明
    Authorizationheaderstringnone
    bodybodyobjectnone
    » courseIdbodyintegernone
    » leavePlacebodystringnone
    » appealBeginTimebodystring2023-10-27T15:50:00
    » appealEndTimebodystring2023-10-27T15:50:00
    » reasonbodystringnone
    » statusbodystring0为未通过,1为通过,2为不通过,传0,到我这都会改成0
    » leaveImagebodystring(binary)请假图片

    返回示例

    {
      "code": 1,
      "msg": "success",
      "data": null
    }
    

    (2)实现代码

        @PreAuthorize("hasAuthority('sys:student:operation')")
        @PostMapping("")
        @ApiOperation("学生请假")
        public Result addLeave(@RequestHeader String Authorization, 
                               @RequestParam Integer courseId, @RequestParam String leavePlace,
                               @RequestParam @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") LocalDateTime appealBeginTime,
                               @RequestParam @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") LocalDateTime appealEndTime, 
                               @RequestParam String reason,
                               @RequestParam String status, 
                               @RequestParam MultipartFile leaveImage) throws IOException {
            //封装其他数据,这部分不用管
            Claims claims = JwtUtil.parseJwt(Authorization);
            Integer userid = Integer.parseInt(claims.getSubject());
            Map studentInfo = studentService.getStudentInfoByUserId(userid);
            Integer studentId = (Integer) studentInfo.get("id");
            status="0";
            LeaveApplication leaveApplication=new LeaveApplication(null,null,courseId,reason,leavePlace,appealBeginTime,appealEndTime,status,null);
            //调用阿里云OSS工具类,将上传上来的文件存入阿里云,并将url赋值给enity类
            String url = aliOSSUtils.upload(leaveImage);
            leaveApplication.setImage(url);
            //调用service层方法像数据库添加封装好的数据
            leaveService.addLeave(leaveApplication);
            return Result.success();
        }