文章目錄
- 注冊阿里OSS
- 注冊并登錄阿里云賬號
- 開通對象存儲OSS
- 創建Bucket
- 修改權限
- 創建AccessKey
- 全局存儲到你的計算機(可以跳過)
- 查看官方文檔(可以跳過)
- SSM使用
- 引入依賴
- 在spring-mvc.xml中加入配置
- 創建上傳工具類AliOssUtil
- 響應工具類ResultJSON
- 編寫controller
- 編寫前端代碼使用ElmentUI
- 自動上傳
- 代碼編寫
- 結果如下演示
- 手動上傳
- 前端代碼編寫
- 后端代碼編寫
- 結果演示如下
注冊阿里OSS
注冊并登錄阿里云賬號
地址如下:https://www.aliyun.com
注冊后使用支付寶登錄
開通對象存儲OSS
這個很便宜,我10塊錢幾個月才用1分錢。不用擔心過期收費過高。
找到產品。
點擊免費試用
這個頁面往下滑動,找到下圖的然后點擊立即試用。
勾選協議,立即試用。
點擊管理試用
創建Bucket
繼續上面的操作
點擊三個橫杠
搜索對象存儲oss
創建Bucket
寫入項目名稱,其他不用管。
點擊我知道了,確認創建
點擊進入Bucket
如下圖
修改權限
點擊權限控制
將上面的話復制到下面然后點擊確定
點擊設置
點擊公共讀
點擊繼續修改
保存
下面的權限變成公共讀即可
創建AccessKey
右上角頭像,點擊AccessKey。
勾選并點擊繼續
點擊創建AccessKey,
選一種方式驗證
注意:一定一定要保存好,丟了只能重新創建無法找回。
全局存儲到你的計算機(可以跳過)
如果你害怕丟失
以管理員身份打開CMD命令行,執行如下命令,配置系統的環境變量。
set OSS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
set OSS_ACCESS_KEY_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
注意:將上述的ACCESS_KEY_ID 與 ACCESS_KEY_SECRET 的值一定一定一定一定一定一定要替換成自己的 。
執行如下命令,讓更改生效。
setx OSS_ACCESS_KEY_ID "%OSS_ACCESS_KEY_ID%"
setx OSS_ACCESS_KEY_SECRET "%OSS_ACCESS_KEY_SECRET%"
執行如下命令,驗證環境變量是否生效。
echo %OSS_ACCESS_KEY_ID%
echo %OSS_ACCESS_KEY_SECRET%
查看官方文檔(可以跳過)
阿里云oss 對象存儲服務的準備工作我們已經完成了,接下來我們就來完成第二步操作:參照官方所提供的sdk示例來編寫入門程序。
首先我們需要來打開阿里云OSS的官方文檔,在官方文檔中找到 SDK 的示例代碼:
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);}
}
編寫前端代碼使用ElmentUI
自動上傳
代碼編寫
<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類型。@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);}}