5.【SpringBoot3】文件上传

1. 文件上传到本地

需求分析

在用户更换头像或发布文章时,需要携带一个图片的 url 地址,该 url 地址是当用户访问文件上传接口,将图片上传成功后,服务器返回的地址。所以,后台需要提供一个文件上传接口,用来接收前端提交的文件数据,并且返回文件的访问地址。

在这里插入图片描述

接口文档

在这里插入图片描述

文件上传知识回忆

前端页面文件上传三要素:

  • 请求方式必须是 post;
  • 表单的编码类型必须是 multipart/form-data;
  • 文件表单项对应的类型必须是 file。

在这里插入图片描述

这三个要素满足后,用户就可以选择要上传的文件,点击提交按钮,最终把文件内容提交给服务器。当服务器接收到该请求后,如果服务器中的代码是使用 SpringMVC 框架编写的,我们就可以在方法上声明一个 MultipartFile 类型的参数,用来接收上传的文件内容。MultipartFile 提供了很多方法:

在这里插入图片描述
接口实现思路

Controller 中声明用于上传文件的 upload()方法,方法内部需要借助 MultipartFile 提供的 api,把文件内容写入本地磁盘文件,因此方法上要声明一个 MultipartFile 类型的参数。

在这里插入图片描述

现在还没有把文件内容上传到阿里云服务器,我们先把文件存储到服务器本地,保证接口访问是正常的,接口没问题后,再把文件上传到服务器测试。

@RestController
public class FileUploadController {
    @PostMapping("/upload")
    public Result<String> upload(MultipartFile file) throws IOException {
        //把文件内容存储到本地磁盘

        //获取文件原始的名称
        String originalFilename = file.getOriginalFilename();
        //file.transferTo的异常直接抛出去
        //再次存储的时候使用原来的文件名
        file.transferTo(new File("C:\\Users\\xxx\\Desktop\\files\\"+originalFilename));
        return Result.success("url访问地址...");
    }
}

postman 测试:

在这里插入图片描述

点击 select file 选择要上传的文件:

在这里插入图片描述

打开文件:

在这里插入图片描述

上传成功:

在这里插入图片描述

在这里插入图片描述

但是,现在的代码中还存在bug。当我们在 postman 中针对刚刚选择的图片再次点击 send 时,files 文件夹中并没有出现两张图片。原因是后上传的图片与之前上传的图片重名,导致之前的图片被覆盖了。在现实中,很有可能出现需要上传两张同名图片的需求,比如两个用户上传的图片同名。因此这个问题需要解决一下。

解决方式就是保证上传时的文件名唯一:在文件后缀名前面拼接UUID:

在这里插入图片描述

再次上传:

在这里插入图片描述

现在只是通过将文件上传到本地的方式模拟了将文件上传到服务器的过程。下面来介绍将文件上传到服务器的实现方式。

2. 文件上传到云服务器

云服务器会提供很多服务,当计算机连接上云服务器并开通某个服务后后就可以使用该服务了。

在这里插入图片描述

在本节,我们使用阿里云的对象存储服务。

使用云对象存储 OSS(Object Storage Service),可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。

使用第三方服务的通用思路

  • 准备工作:在服务提供商网站上注册用户,开通对应服务
  • 参照官方 SDK 编写入门程序:引入阿里云 OSS 相关的 jar 包,并参照提供的示例代码编写程序
  • 参照入门程序,将功能集成到自己的代码中使用

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

2.1 准备工作

在服务提供商网站上注册用户,开通对应服务

在这里插入图片描述

① 登录之后,点击控制台

在这里插入图片描述

② 查看阿里云提供的所有服务

在这里插入图片描述
在这里插入图片描述

③ 搜索对象存储 OSS

在这里插入图片描述

④ 首次使用 OSS 需要开通
在这里插入图片描述
⑤ 按照提示开通后,开始创建 bucket

在这里插入图片描述

⑥ 填写必要信息,创建 bucket

在这里插入图片描述

⑦ 创建成功后,进入 bucket

在这里插入图片描述

⑧ 概览中展示了 bucket 基本信息和访问地址

在这里插入图片描述

⑨ 获取 AccessKey:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

根据提示一步一步操作就能获取到 AccessKey。至此,准备工作完成。

2.2 参照官方 SDK 编写入门程序

导入阿里云 OSS 相关的依赖坐标,并参照提供的示例代码编写程序。

(1) 导入 OSS 相关依赖坐标

① 左侧点击概览:

在这里插入图片描述

② 滑动到底部,点击 SDK 下载

在这里插入图片描述

③ 找到 Java 版本,点击查看文档,会弹出帮助文档。点击“文档中心打开”

在这里插入图片描述

④ 点击”安装“

在这里插入图片描述

⑤ 将所需依赖坐标导入我们的 pom.xml 文件:

在这里插入图片描述

(2) 参照提供的示例代码编写程序

① 因为是要上传文件,所以点击:对象/文件➡上传文件➡简单上传

在这里插入图片描述

② 此处给出了一个示例代码,copy 一下

在这里插入图片描述

copy 的示例代码如下:

public class Demo {

    public static void main(String[] args) throws Exception {
        // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "examplebucket";
        // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
        String objectName = "exampledir/exampleobject.txt";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);

        try {
            // 填写字符串。
            String content = "Hello OSS,你好世界";

            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));

            // 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
            // ObjectMetadata metadata = new ObjectMetadata();
            // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            // metadata.setObjectAcl(CannedAccessControlList.Private);
            // putObjectRequest.setMetadata(metadata);
           
            // 上传字符串。
            PutObjectResult result = ossClient.putObject(putObjectRequest);            
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}   

③ 修改示例代码

public class Demo {

    @Test
    public void test() throws Exception {
        // Endpoint:bucket概览中的访问端口->外网访问的地域节点
        String endpoint = "https://oss-cn-beijing.aliyuncs.com";
        // 阿里云为了防止AccessKeyId和AccessKeySecret泄露,建议以配置环境变量的方式来使用
        // 这里是测试代码,就不再配置环境变量了,直接定义两个变量来模拟,值都改成自己的
        String ACCESS_KEY_ID="**********";
        String ACCESS_KEY_SECRET="**********";
        // Bucket名称
        String bucketName = "big-event-bucket1";
        // 要存储的对象的名称,这里的对象可以是字符串、图片、视频等
        String objectName = "cat.jpg";

        // 创建OSSClient实例
        OSS ossClient = new OSSClientBuilder().build(endpoint, ACCESS_KEY_ID, ACCESS_KEY_SECRET);

        try {
            // 借助图片输入流对象构建出PutObjectRequest对象
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new FileInputStream("C:\\Users\\xxx\\Desktop\\cat.jpg"));

            // 上传到服务器
            PutObjectResult result = ossClient.putObject(putObjectRequest);
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

(3) 运行代码

上传成功:

在这里插入图片描述

点击图片名称,弹出图片具体信息。复制图片在服务器上的 url。

在这里插入图片描述

在浏览器中输入图片在服务器上的 url,搜索。此时已从服务器上下载了图片。

在这里插入图片描述

虽然上面相对于阿里云提供的示例代码该类很多,但只要完成这个 Demo 的改造,今后在一般情况下需要改的地方就只有下面两处:要存储的对象的名称、输入流中要存储的对象在本地的位置。

在这里插入图片描述

2.3 案例集成 OSS

参照入门程序,将功能集成到自己的代码中使用。

为了使用方便,需要把上面改造成功的代码封装成一个工具类。将来哪里需要使用,直接调用工具类的方法即可。

public class AliOssUtil {
    //这四个一般不变,所以抽取到成员处,声明为静态常量
    public static final String ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";//区域节点
    public static final String ACCESS_KEY_ID="LTAI5t9NJbszDF431vYUkzbh";
    public static final String ACCESS_KEY_SECRET="d82arCTGqH3Turz4HY3NA4a9RufHn0";
    public static final String BUCKET_NAME = "big-event-bucket1";// Bucket名称

    // objectName: 要存储的对象的名称,这里的对象可以是字符串、图片、视频等
    // 方法内部可能发生变化的是objectName和输入流,因此以参数的方式暴露出去
    // 方法要返回图片在服务器上的访问地址,因此返回值是String类型
    public static String upLoadFile(String objectName, InputStream inputStream) throws Exception {

        // 创建OSSClient实例
        OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        String url = ""; //为了返回url,先声明一个空字符串
        try {
            PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, objectName, inputStream);
            PutObjectResult result = ossClient.putObject(putObjectRequest);//上传到服务器
            //如果下面的代码不报错,就该给url赋值了
            //url组成:https://bucket名称.区域节点(去掉https://)/objectName
            //ENDPOINT.lastIndexOf("/")+1: 在ENDPOINT中,从最后一个"/"的下一个位置开始截取字符串
            url="https://"+BUCKET_NAME+"."+ENDPOINT.substring(ENDPOINT.lastIndexOf("/")+1)+"/"+objectName;
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return url;
    }
}

现在工具类准备好了,以后需要将文件上传阿里云只需调用 upLoadFile() 方法即可。

在 FileUploadController 中,之前我们将文件存储到了本地磁盘,现在不需要了,直接调用 upLoadFile() 方法将文件上传服务器。upLoadFile() 方法需要传递两个参数,filename 可以作为传入的第一个参数 objectName;第二个参数是文件输入流,MultipartFile 提供的 getInputStream() 方法可以提供该参数。最后将 upLoadFile() 方法返回的 url 响应给浏览器。

@RestController
public class FileUploadController {
    @PostMapping("/upload")
    public Result<String> upload(MultipartFile file) throws Exception {
        //把文件内容上传到阿里云服务器

        //获取原始文件名
        String originalFilename = file.getOriginalFilename();
        //使用UUID生成文件名,保证文件名唯一,从而防止文件被覆盖
        //originalFilename.lastIndexOf("."): 从文件名的最后一个"."处开始截取字符串(包括"."),即获取原始文件后缀名
        String filename = UUID.randomUUID().toString()+originalFilename.substring(originalFilename.lastIndexOf("."));
        // file.transferTo(new File("C:\\Users\\xxx\\Desktop\\files\\"+filename));
        //upLoadFile()方法上的异常直接抛出去即可
        String url = AliOssUtil.upLoadFile(filename, file.getInputStream());
        return Result.success(url);
    }
}

postman 测试:

在这里插入图片描述

在浏览器中输入文件访问地址,可以下载:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/349418.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

85 总结一下最近遇到的一些 jar发布 相关的知识

前言 呵呵 最近有一些构建服务, 发布服务的一些需求 我们这里的服务 一般来说是 java application, spring boot application 针对发布, 当然最好是 增量发布, 尽量的减少需要传递给 发布服务器 的资源的大小 比如 我的这个 java application, 可能会存在很多依赖, 常规…

C++实用教程(四):面向对象核心多态 笔记

推荐B站视频&#xff1a;C现代实用教程(四):面向对象核心多态_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV15v4y1M7fF/?spm_id_from333.999.0.0&vd_sourcea934d7fc6f47698a29dac90a922ba5a3 本项目通用的tasks.json文件和launch.json tasks.json {"versi…

【并发编程】 synchronized的普通方法,静态方法,锁对象,锁升级过程,可重入锁,非公平锁

目录 1.普通方法 2.静态方法 3.锁对象 4.锁升级过程 5.可重入的锁 6.不公平锁 非公平锁的 lock 方法&#xff1a; 1.普通方法 将synchronized修饰在普通同步方法&#xff0c;那么该锁的作用域是在当前实例对象范围内,也就是说对于 SyncDemosdnewSyncDemo();这一个实例对象…

基于Spring Boot的饮食分享平台设计与实现

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

springboot-mybatis项目

一、后端开发环境搭建 1、File->New->Projet 2选择 Spring Initializr &#xff0c;然后选择默认的 url 点击next 3勾选Spring Web、SQL模板&#xff0c;next 4点击finish&#xff0c;搭建完成 二 数据库 1 新建数据库 2 执行sql建表 SET NAMES utf8mb4; SET FOREIGN…

中移(苏州)软件技术有限公司面试问题与解答(4)—— virtio所创建的设备2

接前一篇文章&#xff1a;中移&#xff08;苏州&#xff09;软件技术有限公司面试问题与解答&#xff08;4&#xff09;—— virtio所创建的设备1 在上一篇文章中&#xff0c;对于面试所提出的问题“virtio会创建哪些设备&#xff1f;”&#xff0c;有了初步答案&#xff0c;即…

0125-1-vue3初体验

vue3尝鲜体验 初始化 安装vue/clinext&#xff1a; yarn global add vue/clinext # OR npm install -g vue/clinext然后在 Vue 项目运行&#xff1a; vue upgrade --next项目目录 vue3-template ├── index.html // html模板 ├── mock // mock数据 │ └── user.…

【前沿技术杂谈:NLP技术的发展与应用】探索自然语言处理的未来

【前沿技术杂谈&#xff1a;NLP技术的发展与应用】探索自然语言处理的未来 NLP技术的发展与应用&#xff1a;探索自然语言处理的未来方向一&#xff1a;技术进步词嵌入&#xff08;Word Embeddings&#xff09;Transformer架构自然语言推理 方向二&#xff1a;应用场景智能客服…

深入浅出 diffusion(2):pytorch 实现 diffusion 加噪过程

我在上篇博客深入浅出 diffusion&#xff08;1&#xff09;&#xff1a;白话 diffusion 原理&#xff08;无公式&#xff09;中介绍了 diffusion 的一些基本原理&#xff0c;其中谈到了 diffusion 的加噪过程&#xff0c;本文用pytorch 实现下到底是怎么加噪的。 import torch…

微软 Power Apps model drven app 模型驱动应用使用Plugin插件实现业务流程跳转阶段功能

微软 Power Apps model drven app 模型驱动应用使用Plugin插件实现业务流程跳转阶段功能 模型驱动应用使用插件实现跳转业务流程阶段跳转功能 在实际操作中总会遇到使用业务流程的需求&#xff0c;那么如何使用plugin实现跳转阶段的功能呢 需求背景是主表上有业务流程&#x…

解密Java并发中的秘密武器:LongAdder与Atomic类型

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 解密Java并发中的秘密武器&#xff1a;LongAdder与Atomic类型 前言引言&#xff1a;为何需要原子操作&#xff1f;挑战和问题&#xff1a;原子操作的概念和重要性&#xff1a; AtomicInteger和AtomicL…

leetcode hot100 组合总和Ⅲ

本题中&#xff0c;要求我们求在1-9范围内&#xff0c;满足k个数的和为n的组合&#xff08;组合是无序的&#xff0c;并且题目中要求不可以重复&#xff09;。 这种组合问题依旧需要用回溯算法来解决。因为我们没办法控制产生k层for循环。回溯算法的过程是构建树结构&#xff…

【并发编程】锁死的问题——如何解决?以及如何避免?

目录 1.如何解决 一、死锁的定义和原因 1.1 定义 1.2 原因 二、常见的死锁场景 2.1 线程间相互等待资源 2.2 嵌套锁的循环等待 2.3 对资源的有序请求 三、死锁排查的方法 3.1 使用jstack命令 3.2 使用jconsole 3.3 使用VisualVM 四、常见的解决方案 4.1 避免嵌套锁…

16、Kafka ------ SpringBoot 整合 Kafka (配置 Kafka 属性 及对应的 属性处理类 解析)

目录 配置 Kafka 及对应的 属性处理类配置KafkaKafka配置属性的约定代码演示生产者相关的配置消费者相关的配置 代码&#xff08;配置文件&#xff09;application.properties 配置 Kafka 及对应的 属性处理类 配置Kafka spring.kafka.* 开头的配置属性&#xff0c;这些属性将由…

【Vue2 + ElementUI】分页el-pagination 封装成公用组件

效果图 实现 &#xff08;1&#xff09;公共组件 <template><nav class"pagination-nav"><el-pagination class"page-area" size-change"handleSizeChange" current-change"handleCurrentChange":current-page"c…

ChatGPT模型大更新!全新大、小文本嵌入模型,API价格大降价!

1月26日凌晨&#xff0c;OpenAI在官网对ChatGPT Turbo模型&#xff08;修复懒惰行为&#xff09;&#xff0c;免费的审核模型&#xff0c;并对新的GPT-3.5 Turbo模型API进行了大幅度降价。模型进行了大更新&#xff0c;发布了两款全新大、小文本嵌入模型&#xff0c;全新的GPT-…

600条最强Linux命令总结,建议收藏

今天&#xff0c;带来一篇 Linux 命令总结的非常全的文章&#xff0c;也是我们平时工作中使用率非常高的操作命令&#xff0c;命令有点多&#xff0c;建议小伙伴们可以先收藏后阅读。 在此之前先给大家分享一波黑客学习资料 1. 基本命令 uname -m 显示机器的处理器架构 uname …

超级万能DIY模块化电商小程序源码系统 带完整的搭建教程

随着电商市场的不断扩大&#xff0c;越来越多的商家涌入电商平台&#xff0c;竞争愈发激烈。为了在众多竞争对手中脱颖而出&#xff0c;商家需要打造一款个性化、功能强大的电商小程序&#xff0c;以吸引更多的用户。而超级万能DIY模块化电商小程序源码系统正是为了满足商家的这…

第二证券:大金融板块逆势护盘 北向资金尾盘加速净流入

周一&#xff0c;A股商场低开低走&#xff0c;沪指收盘失守2800点。截至收盘&#xff0c;上证综指跌2.68%&#xff0c;报2756.34点&#xff1b;深证成指跌3.5%&#xff0c;报8479.55点&#xff1b;创业板指跌2.83%&#xff0c;报1666.88点。沪深两市合计成交额7941亿元&#xf…

基于Servlet实现博客系统

目录 一、功能和效果 1、实现的功能 2、页面效果 二、功能具体实现 1、数据库 &#xff08;1&#xff09;设计数据库 &#xff08;2&#xff09;创建数据库表 &#xff08;3&#xff09;实现对blogs表和users表的操作并封装 2、登陆功能实现 &#xff08;1&#xff09…