方式一:
一. OSS
Oss对象存储的优势体现在:
本张图片就存放于七牛云上
二. 需求
现在, 用户可以提交意见反馈给后台, 反馈的内容同时可携带多张图片. 我们不想在数据库中直接保存图片的base64格式的字符, 因为如果图片很大, 那么数据库负担很重.
所以我们使用oss对象存储服务
三. 实现
实现的流程为
1. 输入表单
用户输入完表单, 选择好了要上传的图片, 点击 提交反馈 发送请求
2. 前端发送图片上传的请求
该请求需设置请求头Content-Type: multipart/form-data; 请求体为form-data的file类型, 同时可以选择多张要上传的图片
3. 同时, 后端Controller接收请求
package com.macro.mall.portal.controller;
import com.macro.mall.common.api.CommonResult; import com.macro.mall.portal.domain.ImagesFile; import com.macro.mall.portal.util.QiniuUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.UUID;
@Controller @Api(tags = "UploadFileController", description = "图片上传") @RequestMapping("/upload") public class UploadFileController {
@ApiOperation("上传图片") @RequestMapping(value = "/images", method = RequestMethod.POST) @ResponseBody public CommonResult upload(@RequestParam("imgFile") MultipartFile[] multipartFiles) { ArrayList imgFile = new ArrayList<>(); for (MultipartFile multipartFile : multipartFiles) { ImagesFile imagesFile = new ImagesFile(); String originalFileName = multipartFile.getOriginalFilename(); imagesFile.setImageName(originalFileName); String fileName = UUID.randomUUID().toString().replace("-", "") + "." + getPicSuffix(originalFileName); try (InputStream is = multipartFile.getInputStream()) { QiniuUtils.upload2Qiniu(is, fileName); } catch (RuntimeException| IOException e) { CommonResult.failed(); } String pic = QiniuUtils.QINIU_IMG_URL_PRE + fileName; imagesFile.setImageUrl(pic); imgFile.add(imagesFile); } return CommonResult.success(imgFile); }
public static String getPicSuffix(String imgPath){ if (imgPath == null || imgPath.indexOf(".") == -1){ return ""; } return imgPath.substring(imgPath.lastIndexOf(".") + 1). trim().toLowerCase(); } }
|
值得注意的是, 这里需要用注解@RequestParam(“参数名”) MultipartFile[] 来接收多张图片
multipartFile.getOriginalFilename();可以用来获取上传文件的名字以及后缀.
这里用到了七牛云上传工具类:
package com.macro.mall.portal.util;
import com.qiniu.common.QiniuException; import com.qiniu.http.Response; import com.qiniu.storage.BucketManager; import com.qiniu.storage.Configuration; import com.qiniu.storage.Region; import com.qiniu.storage.UploadManager; import com.qiniu.util.Auth; import lombok.extern.slf4j.Slf4j;
import java.io.IOException; import java.io.InputStream; import java.util.Properties;
@Slf4j public class QiniuUtils { public static final String CONFIG_FILE = "qiniu-config.properties"; public static String ACCESS_KEY; public static String SECRET_KEY; public static String QINIU_IMG_URL_PRE; public static String BUCKET;
static { Properties prop = new Properties(); try(InputStream is = QiniuUtils.class.getClassLoader().getResourceAsStream("qiniu-config.properties")) { if(null == is){ log.error("[七牛云工具类-初始化]失败,请提供配置文件:{}",CONFIG_FILE); }else { prop.load(is); } } catch (IOException e) { throw new RuntimeException(e); } ACCESS_KEY = prop.getProperty("access.key", ""); SECRET_KEY = prop.getProperty("secret.key", ""); QINIU_IMG_URL_PRE = prop.getProperty("img.url.prefix", ""); BUCKET = prop.getProperty("bucket", ""); log.info("[七牛云工具类-初始化]完成"); }
public static void upload2Qiniu(InputStream is, String uploadFileName) throws QiniuException { Configuration cfg = new Configuration(Region.autoRegion()); UploadManager uploadManager = new UploadManager(cfg);
String key = uploadFileName; Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY); String upToken = auth.uploadToken(BUCKET); try { Response response = uploadManager.put(is, key, upToken, null, null); log.info(response.bodyString()); log.info("{}/{}", QINIU_IMG_URL_PRE, uploadFileName); } catch (QiniuException ex) { Response r = ex.response; log.error(r.toString()); try { log.error(r.bodyString()); } catch (QiniuException ex2) { log.error("", ex2); } throw ex; } }
public static void deleteFileFromQiniu(String fileName) throws QiniuException { Configuration cfg = new Configuration(Region.autoRegion()); String key = fileName; Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY); BucketManager bucketManager = new BucketManager(auth, cfg); try { bucketManager.delete(BUCKET, key); } catch (QiniuException ex) { log.error("code:{}", ex.code()); log.error(ex.response.toString()); throw ex; } } }
|
配置文件qiniu-config.properties
在resource文件夹下
img.url.prefix=专属外链 access.key=七牛云的AK secret.key=七牛云的SK bucket=要上传到桶的名字
|
4. 前端收到结果
{ "code": 200, "message": "操作成功", "data": [ { "imageName": "timg (1).jpeg", "imageUrl": "http://q850xek50.bkt.clouddn.com/6ce55d289d8f499783d86771bbf2a074.jpeg" }, { "imageName": "timg (2).jpeg", "imageUrl": "http://q850xek50.bkt.clouddn.com/3deb81d57fa643c5ac9948d6ca72e59e.jpeg" }, { "imageName": "timg.jpeg", "imageUrl": "http://q850xek50.bkt.clouddn.com/327fa1b7964346588bdec1b28154e619.jpeg" } ] }
|
5. 再次发送请求
此时, 请求体中带着反馈的内容和图片的url等内容, 交由后端保存到数据库中.
{ "feedback": " ", "questionType": " ", "images": " " ... }
|
方式二:
前端直接向阿里云OSS发送上传图片请求
Controller:
package com.macro.mall.controller;
import com.macro.mall.common.api.CommonResult; import com.macro.mall.dto.OssCallbackResult; import com.macro.mall.dto.OssPolicyResult; import com.macro.mall.service.impl.OssServiceImpl; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@Controller @Api(tags = "OssController", description = "Oss管理") @RequestMapping("/aliyun/oss") @CrossOrigin public class OssController { @Autowired private OssServiceImpl ossService;
@ApiOperation(value = "oss上传签名生成") @RequestMapping(value = "/policy", method = RequestMethod.GET) @ResponseBody public CommonResult policy() { OssPolicyResult result = ossService.policy(); return CommonResult.success(result); }
@ApiOperation(value = "oss上传成功回调") @RequestMapping(value = "callback", method = RequestMethod.POST) @ResponseBody public CommonResult callback(HttpServletRequest request) { OssCallbackResult ossCallbackResult = ossService.callback(request); return CommonResult.success(ossCallbackResult); }
}
|
OssServiceImpl:
前端请求后台生成 许可
@Override public OssPolicyResult policy() { OssPolicyResult result = new OssPolicyResult(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); String dir = ALIYUN_OSS_DIR_PREFIX + sdf.format(new Date()); long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000; Date expiration = new Date(expireEndTime); long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024; OssCallbackParam callback = new OssCallbackParam(); callback.setCallbackUrl(ALIYUN_OSS_CALLBACK); callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}"); callback.setCallbackBodyType("application/x-www-form-urlencoded"); String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT; try { PolicyConditions policyConds = new PolicyConditions(); policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize); policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); byte[] binaryData = postPolicy.getBytes("utf-8"); String policy = BinaryUtil.toBase64String(binaryData); String signature = ossClient.calculatePostSignature(postPolicy); String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf-8")); result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId()); result.setPolicy(policy); result.setSignature(signature); result.setDir(dir); result.setCallback(callbackData); result.setHost(action); } catch (Exception e) { LOGGER.error("签名生成失败", e); } return result; }
|
配置文件:
aliyun: oss: endpoint: # oss对外服务的访问域名 accessKeyId: # 访问身份验证中用到用户标识 accessKeySecret: # 用户用于加密签名字符串和oss用来验证签名字符串的密钥 bucketName: # oss的存储空间 policy: expire: 300 # 签名有效期(S) maxSize: 10 # 上传文件大小(M) callback: http://localhost:8080/aliyun/oss/callback # 文件上传成功后的回调地址 dir: prefix: mall/images/ # 上传文件夹路径前缀
|
阿里云上传成功后的回调函数
@Override public OssCallbackResult callback(HttpServletRequest request) { OssCallbackResult result= new OssCallbackResult(); String filename = request.getParameter("filename"); filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename); result.setFilename(filename); result.setSize(request.getParameter("size")); result.setMimeType(request.getParameter("mimeType")); result.setWidth(request.getParameter("width")); result.setHeight(request.getParameter("height")); return result; }
|
前端通过回调函数得到存储图片的url路径.