文章目录

  • 说明
  • SSM使用
    • 引入依赖
    • 在spring-mvc.xml中加入配置
    • 创建上传工具类AliOssUtil
    • 响应工具类ResultJSON
    • 编写controller
  • 自动上传
    • 代码编写
    • 结果如下演示
  • 手动上传
    • 前端代码编写
    • 后端代码编写
    • 结果演示如下

说明

为了方便演示,前后端代码一起写了
关于对象存储请看我另一篇博客
阿里云对象存储OSS的使用

SSM使用

引入依赖

<!--阿里云OSS依赖--><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.17.4</version></dependency><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version></dependency><dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency><!-- no more than 2.3.3--><dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><version>2.3.3</version></dependency><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.4</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency>

在spring-mvc.xml中加入配置

<!-- 配置 MultipartResolver 用于文件上传 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 设置最大上传文件大小 --><property name="maxUploadSize" value="10485760"/> <!-- 10MB --><property name="maxInMemorySize" value="4096"/><property name="defaultEncoding" value="UTF-8"/></bean>

创建上传工具类AliOssUtil

package com.Teenage_education_network.utils;import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;import java.io.InputStream;public class AliOssUtil {private static final String ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";private static final String ACCESS_KEY_ID = "你的id";private static final String SECRET_ACCESS_KEY = "你的秘钥";private static final String BUCKET_NAME = "项目名";/** uploadFile方法* objectName:文件名称比如 "YEjdihp893bif1.jpg"* inputStream:文件流,用于读取文件比如,D:\Users\Administrator\Desktop\YEjdihp893bif1.jpg* *///上传文件,返回文件的公网访问地址public static String uploadFile(String objectName, InputStream inputStream){// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(ENDPOINT,ACCESS_KEY_ID,SECRET_ACCESS_KEY);//公文访问地址String url = "";try {// 创建存储空间。ossClient.createBucket(BUCKET_NAME);ossClient.putObject(BUCKET_NAME, objectName, inputStream);// 这里是返回阿里云的url地址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;}
}

响应工具类ResultJSON

package com.hsh.pojo.tdo;import java.io.Serializable;/*** @Author: wzy* @Date: 2024/11/13 11:03* @Description: 返回结果类*/
public class ResultJSON<T> implements Serializable {private Integer code;private String msg;private T data;public ResultJSON(Integer code, String msg, T data) {this.code = code;this.msg = msg;this.data = data;}/*** 操作成功或者失败* @param c 受影响行数* @return 当前传入的受影响行数>0则返回成功,否则返回失败*/public static  ResultJSON successORerror(int c){return c>0?new ResultJSON(200,"操作成功",c):new ResultJSON(400,"操作失败",c);}public static ResultJSON success(){return new ResultJSON(200,"操作成功",null);}public static ResultJSON success(String msg){return new ResultJSON(200,msg,null);}public static <T> ResultJSON success(T data){return new ResultJSON(200,"操作成功",data);}public static ResultJSON success(Integer code,String msg){return new ResultJSON(code,msg,null);}public static <T> ResultJSON success(String msg,T data){return new ResultJSON(200,msg,data);}public static <T> ResultJSON success(Integer code,String msg,T data){return new ResultJSON(code,msg,data);}public static ResultJSON error(){return new ResultJSON(500,"操作失败",null);}public static ResultJSON error(String msg){return new ResultJSON(500,msg,null);}public static ResultJSON error(Integer code,String msg){return new ResultJSON(code,msg,null);}public T getData() {return data;}public void setData(T data) {this.data = data;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}

编写controller

package com.hsh.controller;import com.hsh.pojo.tdo.ResultJSON;
import com.hsh.utils.AliOssUtil;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.UUID;/*** @author xrkhy* @date 2025/9/6 19:05* @description*/@RestController
@RequestMapping("/upload")
public class UploadController {// 本次请求通过elementPlus的el-upload组件上传图片,通过el-upload组件的属性发起请求// 前端上传路径action="/api/upload/imgUpload" 要和后端一致 这里的/api是前端的反向代理的标识// 前端的name="img" 是这里的形参名// 前端的请求头添加token  :headers="{'Authorization':tokenStore.token}"@PostMapping("/imgUpload")public ResultJSON<String> imgUpload(@RequestParam("img") MultipartFile img) throws IOException {System.out.println(img);if (img == null || img.isEmpty()) {// 处理文件为空的情况return ResultJSON.error("文件不能为空");}String originalFilename = img.getOriginalFilename();// 生成新的唯一的文件名String fileNmae = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));String url = AliOssUtil.uploadFile(fileNmae, img.getInputStream());System.out.println(url);return ResultJSON.success("添加成功",url);}
}

自动上传

代码编写

<template><el-form:model="productForm"label-width="120px"label-position="right"><!-- 用户基础信息 --><el-form-item label="商品名称" prop="productName"><el-input v-model="productForm.productName"></el-input></el-form-item><el-form-item label="封面图片"><!-- :auto-upload 设置是否自动上传 true自动上传 --><!-- action为你的请求路径:你要替换为你的上传API地址 --><!-- name: 上传的文件字段名 (也就是后端的参数 我这里是img)后端的接收参数如下就是我上面写的UploadControllerpublic ResultJSON<String> imgUpload(@RequestParam("img") MultipartFile img){}--><!-- :on-success="handleAvatarSuccess" 上传成功回调 --><!-- :before-upload="beforeAvatarUpload"上传前校验 --><!--  list-type="picture-card" 文件列表的类型 这里不需要因为已经有<i class="el-icon-plus" v-if="!productForm.imageUrl"></i>代替了--><!-- :show-file-list="false" 是否显示已上传文件列表 --><!-- 除了上面还可以设置响应头,配置如下:headers="{'Authorization':tokenStore.token}"--><el-upload:auto-upload="true"action="http://localhost:8080/upload/imgUpload"name="img":on-success="handleAvatarSuccess":before-upload="beforeAvatarUpload":show-file-list="false"><!-- v-if="!productForm.imageUrl" 是如果上传成功这个+图标(<i class="el-icon-plus"></i>)上传的提示就不显示了  --><istyle="font-size: 20px; border: 1px solid #ccc; padding: 20px"class="el-icon-plus"v-if="!productForm.imageUrl"></i><img style="width: 100px" v-else :src="productForm.imageUrl" /></el-upload></el-form-item><el-form-item label="商品价格" prop="productPrice"><el-input-numberv-model="productForm.productPrice":precision="2":step="0.01"></el-input-number></el-form-item><el-form-item label="商品库存" prop="productStock"><el-input-numberv-model="productForm.productStock"label="描述文字"></el-input-number></el-form-item><!-- 操作按钮 --><el-form-item><el-button type="primary" @click="submitForm">提交</el-button><el-button @click="resetForm">重置</el-button></el-form-item></el-form>
</template><script>
export default {data() {return {productForm: {productId: null,productName: "",imageUrl: "",productPrice: "",productStock: ""},}}methods: {// 上传成功后的回调handleAvatarSuccess(res, file) {this.productForm.imageUrl = res.data;},// 上传前的校验beforeAvatarUpload(file) {const isJPG = file.type === "image/jpeg" || file.type === "image/png";const isLt2M = file.size / 1024 / 1024 < 2;if (!isJPG) {this.$message.error("上传头像图片只能是 JPG 或 PNG 格式!");}if (!isLt2M) {this.$message.error("上传头像图片大小不能超过 2MB!");}return isJPG && isLt2M;},submitForm() {console.log("提交的数据:", this.formData);// 这里在发起请求},}
}
</script>

结果如下演示

在这里插入图片描述
上面数据确实是拿到了,此时在点击提交发送。
在这里插入图片描述

手动上传

前端代码编写

<template><div><h1>图片手动提交</h1><el-form:model="productForm"style="width: 500px"label-width="120px"label-position="right"><!-- 用户基础信息 --><el-form-item label="商品名称" prop="productName"><el-input v-model="productForm.productName"></el-input></el-form-item><el-form-item label="封面图片"><!-- :auto-upload 设置是否自动上传 true自动上传 --><!-- action为你的请求路径:你要替换为你的上传API地址 --><!--  list-type="picture-card" 文件列表的类型 这里不需要因为已经有<i class="el-icon-plus" v-if="!productForm.imageUrl"></i>代替了--><!-- :show-file-list="false" 是否显示已上传文件列表 这里关闭 --><!-- v-if="!productForm.imageUrl" 是如果上传成功这个+图标(<i class="el-icon-plus"></i>)上传的提示就不显示了 --><el-upload:auto-upload="false"action="#":show-file-list="false":on-change="handleImgChange"><istyle="font-size: 20px; border: 1px solid #ccc; padding: 20px"class="el-icon-plus"v-if="!productForm.imageUrl"></i><img style="width: 100px" v-else :src="productForm.imageUrl" /></el-upload><!-- v-if="productForm.imageUrl" 如果图片不存在 img不显示 --><!-- <img v-if="imgURL" :src="imgURL" /> --></el-form-item><el-form-item label="商品价格" prop="productPrice"><el-input-numberv-model="productForm.productPrice":precision="2":step="0.01"></el-input-number></el-form-item><el-form-item label="商品库存" prop="productStock"><el-input-numberv-model="productForm.productStock"label="描述文字"></el-input-number></el-form-item><!-- 操作按钮 --><el-form-item><el-button type="primary" @click="submitForm">提交</el-button></el-form-item></el-form></div>
</template><script>
import axios from "axios";export default {name: "ImageUpload",data() {return {// 表单数据// 注意这里表单不能有字段为null,否则会报错// 比如productId: null, 发送给后端报错productForm: {productId: "",productName: "",imageUrl: "",productPrice: "",productStock: "",imgUrlFile: ""}};},methods: {// handleFileChange(file, fileList) {//   this.fileList = fileList;// },// 提交前实现封面图片预览handleImgChange(uploadFile) {// 预览图片// this.imgUrl = URL.createObjectURL(uploadFile.raw);this.productForm.imageUrl = URL.createObjectURL(uploadFile.raw);console.log(this.productForm.imageUrl);this.productForm.imgUrlFile = uploadFile.raw;// this.productForm.imageUrl = uploadFile.raw;},async submitForm() {const formData = new FormData();// 追加其他表单字段// 遍历 productForm 对象的属性for (const key in this.productForm) {// 将每个属性和值添加到 FormData 中formData.append(key, this.productForm[key]);}// 追加文件字段formData.append("file", this.imgUrlFile);this.clgFromData(formData);const res = await axios.post("http://localhost:8080/product/addProductWithImg",formData// 下面的headers可以不,会自动识别是json还是formdata// {//   headers: {//     "Content-Type": "multipart/form-data"//   }// });console.log(res);},clgFromData(formData) {for (let pair of formData.entries()) {console.log(pair[0] + ", " + pair[1]);}}}
};
</script><style scoped></style>

后端代码编写

package com.hsh.controller;import com.hsh.pojo.Product;
import com.hsh.pojo.tdo.ResultJSON;
import com.hsh.service.ProductService;
import com.hsh.utils.AliOssUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.List;
import java.util.UUID;@RestController
@RequestMapping("/product")
@CrossOrigin(origins = "http://localhost:8081")
public class ProductController {@AutowiredProductService productService;@PostMapping("/addProduct")public ResultJSON addProduct(@RequestBody Product product){System.out.println("product = " + product);return productService.addProduct(product);}// 注意 传入的product对象的属性不能是 null 也不能是 MultipartFile,否则报400的错误// 注意:java的product对象中,没有imgUrlFile属性。// 前端传入的product对象中,imgUrlFile属性是MultipartFile类型。// 下面的@RequestParam也可换成@RequestPart注解@PostMapping("/addProductWithImg")public ResultJSON<Product> findProductById(@ModelAttribute Product product,@RequestParam(value = "imgUrlFile",required = false) MultipartFile imgUrlFile) throws IOException {System.out.println("product = " + product);System.out.println("imgFile = " + imgUrlFile);if (imgUrlFile == null || imgUrlFile.isEmpty()) {// 处理文件为空的情况return ResultJSON.error("文件不能为空");}String originalFilename = imgUrlFile.getOriginalFilename();// 生成新的唯一的文件名String fileNmae = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));String url = AliOssUtil.uploadFile(fileNmae, imgUrlFile.getInputStream());System.out.println(url);product.setImageUrl(url);return productService.addProduct(product);}}

结果演示如下

在这里插入图片描述

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

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

相关文章

Langchain4j 整合MongoDB 实现会话持久化存储详解

目录 一、前言 二、大模型会话记忆介绍 2.1 AI 大模型会话记忆是什么 2.2 大模型会话记忆常用实现方案 2.3 LangChain4j 会话记忆介绍 三、大模型常用会话存储数据库介绍 3.1 常用的会话存储数据库 3.2 MongoDB 简介 3.2.1 MongoDB 是什么 3.3 为什么选择MongoDB 作为…

SQL 常用 OVER() 窗口函数介绍

1. sum() over() 做组内数据累加在 SQL 中想实现不同分组内数据累加&#xff0c;可以通过 sum() over() PARTITION BY ORDER BY 结合实现。这种方式能同时满足多维度分组且组内累加的需求&#xff0c;示例如下&#xff1a;假设我们有一张 sales 表&#xff0c;表中存储着…

OpenRouter:一站式 AI 模型调用平台,免费畅享千问、DeepSeek 等顶级模型

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事&#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 OpenRouter&#xff1a;一站式 AI 模型调用平台&#xff0c;免费畅享千问、DeepSeek 等顶级模型前…

SpringBoot 整合 Kafka 的实战指南

引言&#xff1a; 本文总字数&#xff1a;约 9800 字预计阅读时间&#xff1a;40 分钟 为什么 Kafka 是高吞吐场景的首选&#xff1f; 在当今的分布式系统中&#xff0c;消息队列已成为不可或缺的基础设施。面对不同的业务场景&#xff0c;选择合适的消息队列至关重要。目前…

OpenCV 实战篇——如何测算出任一副图片中的物体的实际尺寸?传感器尺寸与像元尺寸的关系?

文章目录1 如何测算出任一副图片中的物体的实际尺寸2 传感器尺寸与像元尺寸的关系3 Max Frame Rate最大帧率4 为什么要进行相机标定?相机标定有何意义?5 基于相机模型的单目测距--普通相机1 如何测算出任一副图片中的物体的实际尺寸 物体尺寸测量的思路是找一个确定尺寸的物…

Java并发锁相关

锁相关 ​1. 什么是可重入锁&#xff1f;Java 中如何实现&#xff1f;​​ ​答​&#xff1a; 可重入锁允许一个线程多次获取同一把锁&#xff08;即递归调用时无需重新竞争锁&#xff09;。 ​关键点​&#xff1a;防止死锁&#xff0c;避免线程因重复请求已持有的锁而阻塞。…

Pie Menu Editor V1.18.7.exe 怎么安装?详细安装教程(附安装包)​

​​Pie Menu Editor V1.18.7.exe​ 是一款用于创建和编辑 ​饼图菜单&#xff08;Pie Menu&#xff09;​​ 的工具软件&#xff0c;通常用于游戏开发、UI设计、3D建模&#xff08;如 Blender 等&#xff09;、或自定义软件操作界面。 一、准备工作 ​下载文件​ 下载了 ​Pi…

基于Spark的中文文本情感分析系统研究

引言 1.1 研究背景与意义 随着互联网的普及和社交媒体的兴起、特别是自媒体时代的来临&#xff0c;网络文本数据呈现爆炸式增长。这些文本数据蕴含着丰富的用户情感信息&#xff0c;如何有效地挖掘和利用这些信息&#xff0c;对于了解舆情动态、改进客户服务、辅助决策分析具…

Simulink子系统、变体子系统及封装知识

1.引言 文章三相新能源并网系统序阻抗模型——序阻抗分析器IMAnalyzer介绍了一种用于分析和扫描序阻抗的软件。其中&#xff0c;在序阻抗扫频操作过程中&#xff0c;用到了一个扰动注入、测量和运算工具【IMtool】&#xff0c;它外表长这样&#xff1a; 内部长这样&#xff1a…

高阶组件介绍

高阶组件约定俗成以with开头 import React, { useEffect } from react; import { TouchableOpacity, Image, StyleSheet } from react-native;type IReactComponent React.ClassicComponentClass| React.ComponentClass| React.FunctionComponent| React.ForwardRefExoticComp…

C++ STL系列-02.泛型入门

C STL系列-02.泛型入门C中的泛型编程主要通过模板&#xff08;template&#xff09;实现。模板允许我们编写与类型无关的代码&#xff0c;是一种将类型作为参数进行编程的方式。在C中&#xff0c;模板分为函数模板和类模板。 1. 函数模板函数模板允许我们定义一个函数&#xff…

高效管理网络段和端口集合的工具之ipset

目录 1. 核心命令速查 2. 集合类型 3. 实战案例&#xff1a;使用 ipset 封禁 IP 案例 1&#xff1a;基础黑名单封禁&#xff08;手动添加&#xff09; 案例 2&#xff1a;自动过期和解封 案例 3&#xff1a;封禁 IP 和端口组合 案例 4&#xff1a;白名单模式 案例 5&am…

实例和对象的区别

对象&#xff08;Object&#xff09;是一个概念&#xff0c;它表示“某个类的一个成员”&#xff0c;是“逻辑上的个体”。实例&#xff08;Instance&#xff09;是一个现实&#xff0c;指的是在内存中真正分配了空间的对象。实例一定是对象&#xff0c;但对象不一定是实例。例…

Win10 Chrome认不出新Emoji?两个扩展搞定显示与输入

前言 用Win10电脑在Chrome里发消息、刷网页时&#xff0c;你是否遇到过这样的尴尬&#xff1a;别人发的、或者页面显示的 Emoji&#xff0c;在你屏幕上变成了空白方框&#xff0c;像“文字里缺了一块拼图”&#xff1f;其实这不是Chrome的错&#xff0c;也不用换电脑&#xff0…

Golang中逃逸现象, 变量“何时栈?何时堆?”

目录 什么是栈 什么是堆 栈 vs 堆&#xff08;核心区别&#xff09; GO编译器的逃逸分析 什么是逃逸分析&#xff1f; 怎么看逃逸分析结果&#xff1f; 典型“会逃逸”的场景 闭包捕获局部变量 返回或保存带有“底层存储”的容器 经由接口/反射/fmt 等导致装箱或被长…

MySQL入门指南:从安装到工作原理

什么是MySQL MySQL是一个开源的关系型数据库管理系统&#xff0c;由瑞典MySQL AB公司开发&#xff08;目前属于Oracle公司&#xff09;&#xff0c;被广泛地应用在大中小型网站中 MySQL是一个小型的开源的关系型数据库管理系统&#xff0c;与其他大型数据库管理系统例如&…

dask.dataframe.shuffle.set_index中获取 divisions 的步骤分析

dask.dataframe.shuffle.set_index 中获取 divisions 的步骤分析 主要流程概述 在 set_index 函数中&#xff0c;当 divisionsNone 时&#xff0c;系统需要通过分析数据来动态计算分区边界。这个过程分为以下几个关键步骤&#xff1a; 1. 初始检查和准备 if divisions is None:…

ai生成ppt工具有哪些?10款主流AI生成PPT工具盘点

随着人工智能技术的飞速发展&#xff0c;AI生成PPT工具逐渐成为职场人士、学生和创作者提升效率的得力助手。这类工具通过智能算法&#xff0c;能够快速将文本、数据或创意转化为结构化、视觉化的演示文稿&#xff0c;大幅节省设计时间。1、AiPPT星级评分&#xff1a;★★★★★…

Qt多线程编程学习

Qt多线程编程学习 1. 项目概述 本项目展示了Qt中多线程编程的基本用法&#xff0c;通过继承QThread类创建自定义线程&#xff0c;并演示了线程的启动、执行和销毁过程。项目包含一个简单的用户界面&#xff0c;用户可以通过按钮控制线程的启动和结束。 1.1 项目结构 项目包含以…

加密货币武器化:恶意npm包利用以太坊智能合约实现隐蔽通信

ReversingLabs研究人员发现两个恶意npm包利用以太坊&#xff08;Ethereum&#xff09;智能合约隐藏并传播恶意软件。这两个名为colortoolsv2和mimelib2的软件包于2025年7月被识别&#xff0c;展现了开源安全攻防战中的新战术。恶意软件包伪装成实用工具攻击活动始于7月7日发布的…