从零搭建微服务项目Pro(第3-1章——本地/OSS图片文件存取)

前言:

在小型demo项目中,一般将图片音频等字节流文件存放本地数据库,但企业级项目中,由于数据量容量有限,需要借助OSS来管理大规模文件。

OSS(对象存储服务,Object Storage Service)是一种用于存储和管理非结构化数据的云存储服务,适合存储图片、视频、文档等大规模数据。它具有高扩展性、高可靠性、低成本和易用性等特点,广泛应用于数据备份、多媒体存储、大数据分析和静态网站托管等场景。使用OSS的基本流程包括:首先在云服务提供商平台创建存储空间(Bucket),然后通过API、SDK或管理控制台上传和管理文件,设置访问权限以确保数据安全,最后可以通过生成的(临时)URL访问存储的文件。

本章在讲解如何使用mysql存储字节型数据后,示范如何使用阿里云OSS服务完成文件的存取,并提供对应前端代码而非postman调用接口。

本章项目源码如下:(注意将application中内容换成3.6部分内容)

wlf728050719/SpringCloudPro3-1https://github.com/wlf728050719/SpringCloudPro3-1 以及本专栏会持续更新微服务项目,每一章的项目都会基于前一章项目进行功能的完善,欢迎小伙伴们关注!同时如果只是对单章感兴趣也不用从头看,只需下载前一章项目即可,每一章都会有前置项目准备部分,跟着操作就能实现上一章的最终效果,当然如果是一直跟着做可以直接跳过这一部分。专栏目录链接如下,其中Base篇为基础微服务搭建,Pro篇为复杂模块实现。

从零搭建微服务项目(全)-CSDN博客https://blog.csdn.net/wlf2030/article/details/145799620


一、基本项目创建

1.创建Spring Boot项目,选择不添加依赖和插件

2.删除不必要目录和文件,创建application.yml

3.替换pom内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.9.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>cn.bit</groupId><artifactId>Pro3_1</artifactId><version>0.0.1-SNAPSHOT</version><name>Pro3_1</name><description>Pro3_1</description><dependencies><!-- Web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.3.9.RELEASE</version></dependency><!-- Mysql驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><!--Mybatis--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.7</version></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope><version>1.18.36</version><optional>true</optional></dependency><!-- test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

4.创建存放图片的数据库和表

表sql如下:

create table tb_img
(id    int auto_incrementprimary key,name  varchar(100) not null,image longblob     not null
);

yml内容如下:(替换为自己的数据库)

server:port: 1234
spring:datasource:url: jdbc:mysql://localhost:3306/db_image?useSSL=falseusername: rootpassword: 15947035212driver-class-name: com.mysql.jdbc.Driverapplication:name: image-servicemybatis-plus:mapper-locations: classpath:mapper/*Mapper.xmltype-aliases-package: cn.bit.pro3_1.mapper

5.配置数据源


二、本地数据库存取文件

1.连接成功后选择Mybatis X Generator自动生成实体类等对应代码。

2.最后生成目录如下:

3.生成mapper上加@Mapper

4.service接口加两个方法

package cn.bit.pro3_1.service;import cn.bit.pro3_1.entity.Image;
import com.baomidou.mybatisplus.extension.service.IService;/**
* @author wlf
* @description 针对表【tb_img】的数据库操作Service
* @createDate 2025-03-10 16:39:32
*/
public interface ImageService extends IService<Image> {int insertImage(Image image);Image selectImageById(int id);
}

5.对应实现

package cn.bit.pro3_1.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.bit.pro3_1.entity.Image;
import cn.bit.pro3_1.service.ImageService;
import cn.bit.pro3_1.mapper.ImageMapper;
import org.springframework.stereotype.Service;/**
* @author wlf
* @description 针对表【tb_img】的数据库操作Service实现
* @createDate 2025-03-10 16:39:32
*/
@Service
public class ImageServiceImpl extends ServiceImpl<ImageMapper, Image>implements ImageService{@Overridepublic int insertImage(Image image) {return baseMapper.insert(image);}@Overridepublic Image selectImageById(int id) {return baseMapper.selectById(id);}
}

6.创建controller包以及ImageController @CrossOrigin必须,否则出现跨域问题导致尽管业务没问题,但响应被浏览器视为错误。

package cn.bit.pro3_1.controller;import cn.bit.pro3_1.entity.Image;
import cn.bit.pro3_1.service.ImageService;
import lombok.AllArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;@RestController
@RequestMapping("/image")
@AllArgsConstructor
@CrossOrigin(origins = "*")
public class ImageController {private final ImageService imageService;/*** 上传图片并存储到数据库** @param file 前端上传的图片文件* @return 图片的 ID*/@PostMapping("/upload")public ResponseEntity<Integer> uploadImage(@RequestParam("file") MultipartFile file) throws IOException {// 将 MultipartFile 转换为 byte[]byte[] imageData = file.getBytes();// 获取文件名String fileName = file.getOriginalFilename();// 创建 Image 对象Image image = new Image();image.setName(fileName);image.setImage(imageData);// 调用 Service 层方法存储图片imageService.insertImage(image);// 返回图片的 IDreturn ResponseEntity.ok(image.getId());}/*** 根据图片 ID 获取图片并返回给前端** @param id 图片的 ID* @return 图片的字节数据*/@GetMapping(value = "/{id}", produces = MediaType.IMAGE_JPEG_VALUE)public ResponseEntity<byte[]> getImage(@PathVariable int id) {// 调用 Service 层方法获取图片Image image = imageService.selectImageById(id);if (image == null) {return ResponseEntity.notFound().build();}// 返回图片的字节数据return ResponseEntity.ok(image.getImage());}
}

7.前端html如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Image Upload and Display</title>
</head>
<body>
<h1>Upload Image to Local Server</h1>
<form id="uploadForm"><input type="file" id="fileInput" name="file" accept="image/*" required><button type="submit">Upload to Local</button>
</form><h1>Upload Image to AliYun OSS</h1>
<form id="aliyunUploadForm"><input type="file" id="aliyunFileInput" name="file" accept="image/*" required><button type="submit">Upload to AliYun OSS</button>
</form><h1>Download Image from AliYun OSS</h1>
<form id="aliyunDownloadForm"><input type="text" id="fileNameInput" placeholder="Enter file name" required><button type="submit">Download from AliYun OSS</button>
</form><h1>Display Image</h1>
<div id="imageContainer"><!-- 图片将显示在这里 -->
</div><script>// 处理本地服务器上传表单提交document.getElementById('uploadForm').addEventListener('submit', async (event) => {event.preventDefault();const fileInput = document.getElementById('fileInput');const file = fileInput.files[0];if (!file) {alert('Please select a file.');return;}const formData = new FormData();formData.append('file', file);try {// 发送上传请求到本地服务器const response = await fetch('http://localhost:1234/image/upload', {method: 'POST',body: formData});if (!response.ok) {throw new Error('Upload failed');}const imageId = await response.json();alert(`Image uploaded successfully! Image ID: ${imageId}`);// 上传成功后显示图片displayImage(imageId);} catch (error) {console.error('Error:', error);alert('Failed to upload image.');}});// 显示本地服务器图片async function displayImage(imageId) {try {const response = await fetch(`http://localhost:1234/image/${imageId}`);if (!response.ok) {throw new Error('Failed to fetch image');}const imageBlob = await response.blob();const imageUrl = URL.createObjectURL(imageBlob);const imageContainer = document.getElementById('imageContainer');imageContainer.innerHTML = `<img src="${imageUrl}" alt="Uploaded Image" style="max-width: 100%;">`;} catch (error) {console.error('Error:', error);alert('Failed to display image.');}}document.getElementById('aliyunUploadForm').addEventListener('submit', async (event) => {event.preventDefault();const fileInput = document.getElementById('aliyunFileInput');const file = fileInput.files[0];if (!file) {alert('Please select a file.');return;}const formData = new FormData();formData.append('file', file);try {// 发送上传请求到阿里云 OSSconst response = await fetch('http://localhost:1234/image/AliYunImgUpload', {method: 'POST',body: formData});if (!response.ok) {throw new Error('Upload to AliYun OSS failed');}const fileName = await response.text();alert(`Image uploaded to AliYun OSS successfully! File Name: ${fileName}`);} catch (error) {console.error('Error:', error);alert('Failed to upload image to AliYun OSS.');}});// 处理阿里云 OSS 下载表单提交document.getElementById('aliyunDownloadForm').addEventListener('submit', async (event) => {event.preventDefault();const fileNameInput = document.getElementById('fileNameInput');const fileName = fileNameInput.value;if (!fileName) {alert('Please enter a file name.');return;}try {// 发送下载请求到阿里云 OSSconst response = await fetch(`http://localhost:1234/image/AliYunImgDownload?fileName=${fileName}`);if (!response.ok) {throw new Error('Failed to fetch image');}const imageUrl = await response.text();alert(`Image downloaded from AliYun OSS successfully! Image URL: ${imageUrl}`);// 显示图片displayImageFromUrl(imageUrl);} catch (error) {console.error('Error:', error);alert('Failed to download image from AliYun OSS.');}});// 显示阿里云 OSS 图片function displayImageFromUrl(imageUrl) {const imageContainer = document.getElementById('imageContainer');imageContainer.innerHTML = `<img src="${imageUrl}" alt="Uploaded Image" style="max-width: 100%;">`;}
</script>
</body>
</html>

8.启动服务

9.前端选择本地图片并上传

10.弹窗提示上传成功,且会马上调用一次回显从数据库取出图片

11.数据库数据成功添加。


三、OSS存取文件

1.阿里云注册账号

2025主会场_智惠采购季 就上阿里云-阿里云采购季https://www.aliyun.com/activity/purchase/purchasing?utm_content=se_10204504842.注册并登录后,搜索ram并创建用户

记得存下id和key,关闭页面后就复制不了了

3.创建后授权

4.购买oss服务,并创建bucket,使用默认的选择即可

创建后点击查看概览

记住红框里面的endpoint值

5.pom添加阿里云oss依赖

<!-- 阿里云对象存储 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alicloud-oss</artifactId><version>2.2.0.RELEASE</version></dependency>

6.修改application.yml内容:(记得替换为自己的数据)

server:port: 1234
spring:datasource:url: jdbc:mysql://localhost:3306/db_image?useSSL=falseusername: rootpassword: 15947035212driver-class-name: com.mysql.jdbc.Driverapplication:name: image-servicecloud:alicloud:access-key: 你的key idsecret-key: 你的key secretoss:endpoint: 你的end pointbucket:name: 你的bucket名
mybatis-plus:mapper-locations: classpath:mapper/*Mapper.xmltype-aliases-package: cn.bit.pro3_1.mapper

7.创建FileController

package cn.bit.pro3_1.controller;import com.aliyun.oss.OSS;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import java.net.URL;
import java.util.Date;@RestController
@CrossOrigin(origins = "*")
public class FileController {@Autowiredprivate OSS ossClient;@Value("${spring.cloud.alicloud.oss.bucket.name}")private String bucketName;@Value("${spring.cloud.alicloud.oss.endpoint}")private String endPoint;/*** 上传文件到阿里云 OSS*/@PostMapping("/image/AliYunImgUpload")public ResponseEntity<String> fileUpload(@RequestParam("file") MultipartFile file) {try {String fileName = new Date().getTime() + "-" + file.getOriginalFilename(); // 使用时间戳避免文件名冲突// 文件上传ossClient.putObject(bucketName, fileName, file.getInputStream());return ResponseEntity.ok(fileName);} catch (Exception e) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());}}/*** 从阿里云 OSS 取回文件*/@GetMapping("/image/AliYunImgDownload")public ResponseEntity<String> fileDownload(@RequestParam("fileName") String fileName) {try {// 设置预签名 URL 的过期时间(例如 1 小时)Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);// 生成预签名 URLGeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, fileName);request.setExpiration(expiration);URL url = ossClient.generatePresignedUrl(request);// 返回预签名 URLreturn ResponseEntity.ok(url.toString());} catch (Exception e) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());}}
}

8.启动服务后前端选择上传文件到oss

9.oss刷新后能看到上传的文件

10.将oss中文件名复制

11.回显成功

最后:

目前仅仅只是通过文件名直接访问oss拿取文件,实际应当先根据用户身份从mysql数据库中拿取用户能够访问的文件名,之后再次访问oss,实际可将oss理解为一个以文件名+桶名为主键的数据库专门用于存储大规模数据以减少本地数据库压力。后续会继续完善以及合并。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot 日志

目录 一、为什么要学习日志 二、认识日志格式 三、日志使用 打印日志 步骤 日志框架介绍 门面模式(外观模式) 门面模式的实现 门面模式的优点 四、日志级别 日志级别分类 日志级别的使用 日志配置 配置日志级别 日志持久化 配置日志文件分割 五、更简单的日志…

linux内存页块划分及位图存储机制

page_alloc.c - mm/page_alloc.c - Linux source code v5.4.285 - Bootlin Elixir Cross Referencer 一. 什么是页块&#xff08;Pageblock&#xff09;&#xff1f; 定义&#xff1a;页块是物理内存中的一个连续区域&#xff0c;由 2^pageblock_order 个物理页&#xff08;Pag…

chebykan与代码3

目录 参考文献有 ‘’ 中文 各自讲了什么 切比雪夫多项式有两类吗&#xff1f;这里存疑 KAN变体 期刊 切比雪夫と爱因斯坦の约定 维度标签的含义 爱因斯坦求和约定 参考文献有 ‘’ 中文 [1] 神经网络&#xff1a;全面基础 [2] 通过sigmoid函数的超层叠近似 [3] 多层前…

DETR详解

1.概述 DETR&#xff0c;全称为Detection Transformer&#xff0c;是Facebook在ECCV2020上提出的基于Transformer的端到端目标检测网络最大的特点就是&#xff1a;不需要预定义的先验anchor&#xff0c;也不需要NMS的后处理策略&#xff0c;就可以实现端到端的目标检测。但是&…

南昌长空STONE 60A-M 无人机电调深度测评:轻量化设计与工业级安全的融合典范

引言 在无人机技术不断革新的今天&#xff0c;电调作为动力系统的核心组件&#xff0c;其性能直接影响飞行稳定性与操控体验。STONE 系列凭借 “轻量化设计” 理念&#xff0c;在竞争激烈的市场中独树一帜。本文将深度解析 STONE 60A-M 电调的技术亮点与实际表现&#xff0c;探…

初阶数据结构(C语言实现)——4.2队列

目录 2.队列2.1队列的概念及结构2.2队列的实现2.2.1 初始化队列2.2.2 销毁队列2.2.3 队尾入队列2.2.4 队头出队列2.2.5获取队列头部元素2.2.6 获取队列队尾元素2.2.7获取队列中有效元素个数2.2.8 检测队列是否为空&#xff0c;如果为空返回非零结果&#xff0c;如果非空返回0 3…

C++和OpenGL实现3D游戏编程【连载24】——父物体和子物体之间的坐标转换

欢迎来到zhooyu的C++和OpenGL游戏专栏,专栏连载的所有精彩内容目录详见下边链接: 🔥C++和OpenGL实现3D游戏编程【总览】 父子物体的坐标转换 1、本节要实现的内容 前面章节我们了解了父物体与子物体的结构,它不仅能够表示物体之间的层次关系,更重要的一个作用就是展示物…

怎么实现: 大语言模型微调案例

怎么实现: 大语言模型微调案例 目录 怎么实现: 大语言模型微调案例输入一个反常识的问题:首都在北京天安门之后对输出模型进行测试:首都在北京天安门微调代码:测试微调模型代码:微调输出模型结构输出模型参数大小对比Qwen 2.5_0.5:53MB输出模型:951MB 是一样的,没有进行…

知乎后台管理系统:数据库系统原理实验1——数据库基础概念

实验背景 通过练习绘制语义网络&#xff0c;加深对于基本概念之间关系的理解和掌握。掌握在VISIO中绘制能准确表达基本概念之间关系的语义网络的技能。了解并比较数据模型的Chen’s表示法和UML表示法。理解关系模型设计中的完整性约束的重要性。掌握在Linux操作系统下远程访问…

超过 37000 台 VMwareESXi 服务器可能受到持续攻击威胁

近日&#xff0c;威胁监测平台影子服务器基金会&#xff08;The Shadowserver Foundation&#xff09;发布报告&#xff0c;指出超 3.7 万个互联网暴露的威睿&#xff08;VMware&#xff09;ESXi 实例存在严重安全隐患&#xff0c;极易受到 CVE-2025-22224 漏洞的攻击。该漏洞属…

Linux《基础开发工具(中)》

在之前的Linux《基础开发工具&#xff08;上&#xff09;》当中已经了解了Linux当中到的两大基础的开发工具yum与vim&#xff1b;了解了在Linux当中如何进行软件的下载以及实现的基本原理、知道了编辑器vim的基本使用方式&#xff0c;那么接下来在本篇当中将接下去继续来了解另…

Vue3 Pinia 符合直觉的Vue.js状态管理库

Pinia 符合直觉的Vue.js状态管理库 什么时候使用Pinia 当两个关系非常远的组件&#xff0c;要传递参数时使用Pinia组件的公共参数使用Pinia

知识库Dify和cherry无法解析影印pdf word解决方案

近期收到大量读者反馈&#xff1a;上传pdf/图文PDF到Dify、Cherry Studio等知识库时&#xff0c;普遍存在格式错乱、图片丢失、表格失效三大痛点。 在试用的几款知识库中除了ragflow具备图片解析的能力外&#xff0c;其他的都只能解析文本。 如果想要解析扫描件&#xff0c…

Webservice创建

Webservice创建 服务端创建 3层架构 service注解&#xff08;commom模块&#xff09; serviceimpl&#xff08;server&#xff09; 服务端拦截器的编写 客户端拦截器 客户端调用服务端&#xff08;CXF代理&#xff09; 客户端调用服务端&#xff08;动态模式调用&a…

腾讯云低代码开发应用

创建客户端应用 如上所示&#xff0c;登录腾讯云微搭低代码业务控制台&#xff0c;开始搭建企业官网应用 如上所示&#xff0c;在腾讯云微搭低代码业务控制台中&#xff0c;开始创建企业官网应用 如上所示&#xff0c;在腾讯云微搭低代码业务控制台中&#xff0c;开始编辑企业官…

【Java开发指南 | 第三十四篇】IDEA没有Java Enterprise——解决方法

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 1、新建Java项目2、单击项目名&#xff0c;并连续按两次shift键3、在搜索栏搜索"添加框架支持"4、勾选Web应用程序5、最终界面6、添加Tomcat 1、新建Java项目 2、单击项目名&#xff0c;并连续按两次…

深度学习原理与Pytorch实战

深度学习原理与Pytorch实战 第2版 强化学习人工智能神经网络书籍 python动手学深度学习框架书 TransformerBERT图神经网络&#xff1a; 技术讲解 编辑推荐 1.基于PyTorch新版本&#xff0c;涵盖深度学习基础知识和前沿技术&#xff0c;由浅入深&#xff0c;通俗易懂&#xf…

uniapp项目运行失败Error: getaddrinfo *.bspapp.com 文件查找失败uview-ui及推荐MarkDown软件 Typora

一、uniapp项目运行失败Error: getaddrinfo *.bspapp.com 文件查找失败uview-ui 在运行一个uniapp项目时&#xff0c;出现报错 文件查找失败&#xff1a;uview-ui&#xff0c;Error: getaddrinfo ENOTFOUND 960c0a.bspapp.com。hostname异常&#xff0c;报错的详细信息如下&…

什么是vue的keep-alive?它是如何实现的?具体缓存了什么内容?

文章目录 一、keep-alive 的核心作用二、实现原理1. 缓存管理策略2. 核心源码解析&#xff08;Vue 2.x 简化版&#xff09;3. 缓存生命周期 三、缓存的具体内容1. 缓存对象结构2. 具体缓存内容 四、使用示例1. 基础用法2. 配置缓存策略 五、注意事项六、实现流程图解 Vue 的 k…

pytest基础知识

pytest知识了解 pytest的基础知识了解&#xff1a;Python测试框架之pytest详解_lovedingd的博客-CSDN博客_pytest框架 (包含设置断点&#xff0c;pdb&#xff0c;获取最慢的10个用例的执行耗时) pytest-pytest.main()运行测试用例&#xff0c;pytest参数&#xff1a; pytest-…