本次我们使用 Spring Boot 实现一个图片上传的接口,POST方式请求接口,Header中携带上传者的用户标识(uid
) ,图片上传完毕后返回上传图片的信息(地址、宽度、高度)。JDK版本为 8 。
图片上传一般会需要一个文件服务器,用来专门存储上传的文件,但有时如果需要对存储的文件实现更快速的上传和下载功能,并且又对可靠性、扩展性有很高的要求,在前期不打算投入过多情况下,可以考虑使用第三方提供的云服务,快速实现这个需求。
本次我们使用 七牛云提供的存储服务来实现这个需求,其控制台比较直观,接口开发测试方便,关于收费可以参考其官网 产品价格。
点击首页的 注册有礼,进入主页页面,填写邮箱、账号密码、手机号、短信验证,用户类型(个人用户或企业用户)、现居省份和城市,点击议下一步进行注册。
邮箱收到激活连接后点击激活,激活后登陆,此时需要实名认证,上传身份证的正面和背面图片完成实名认证。
点击页头右侧的 【管理控制台】-> 点击右上角的头像 -> 密钥管理。如下图
鼠标滑到左侧边栏,点击【对象存储】,如果提示是否开通,选择开通。第一次使用时需要新建存储空间,填写存储空间名称(这个就是后面的 bucket 名),存储区域可以默认的 【华南】,访问控制可以选择【公开空间】,这个后期可以改为私有空间,,点击【确定创建】。
七牛云会默认生成一个域名,此域名可用于开发测试,测试域名的生命周期为30日,过期后系统会自动回收。
点击 【融合CDN】 -> 域名管理 ,可以看到此域名的基本信息,更多七牛测试域名的使用规范可查看官方的文档:七牛测试域名使用规范。
至此我们已经设置好了七牛云的服务信息,这个也是我们调用七牛云服务时需要用到的常量参数,因此我们创建一个常量类,代码如下:
package yore.utils;
/**
* 七牛云服务的常量类
* Created by yore on 2019/12/7 17:52
*/
public interface QiniuConstant {
/** 七牛AK */
String ACCESS_KEY = "VXu6olBiuH********************AWQhHvVO6b";
/** 七牛SK */
String SECRET_KEY = "0Cjg_UCtpQ********************ZjWIPoI5zh";
/** 七牛存储空间名 */
String BUCKET = "yoreyuan";
/** 七牛默认生成的域名 */
String DOMAIN = "http://q24******.bkt.clouddn.com";
}
使用 IntelliJ IDEA 创建一个Maven 项目,引入 Spring Boot 需要的依赖。
在我们引入spring-boot-starter-web
依赖时,会默认加入 jackson-databind 作为 JSON处理器,这里我们统一使用 fastjson 处理项目的 JSON 数据。
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.2.RELEASEversion>
parent>
<groupId>yore.springbootgroupId>
<artifactId>qiniu-uploadartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>jarpackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<spring-boot.version>2.0.2.RELEASEspring-boot.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.20version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>com.qiniugroupId>
<artifactId>qiniu-java-sdkartifactId>
<version>7.2.23version>
dependency>
<dependency>
<groupId>com.squareup.okhttp3groupId>
<artifactId>okhttpartifactId>
<version>3.14.2version>
<scope>compilescope>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.51version>
dependency>
<dependency>
<groupId>com.qiniugroupId>
<artifactId>happy-dns-javaartifactId>
<version>0.1.6version>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring-boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
#端口号
server:
port: 8081
tomcat:
uri-encoding: UTF-8
##datasource
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
sql-script-encoding: UTF-8
type: com.alibaba.druid.pool.DruidDataSource
jpa:
hibernate:
ddl-auto: update
show-sql: true
database: mysql
messages:
encoding: UTF-8
http:
encoding:
force-response: true
###############
#My Configuration
###############
my:
qiniu:
image:
namespace: yore/image/ # 文件上传的命名空间
multipart:
maxFileSize: 10Mb
maxRequestSize: 10Mb
package yore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Created by yore on 2019/12/7 15:55
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package yore.utils;
/**
* Created by yore on 2019/11/2 16:28
*/
public enum MsgEnum {
SUCCESS_1(200, "文件上传成功"),
OK_1(400, "请先登录"),
OK_2(401, "文件非图片"),
OK_3(402, "图片上传失败"),
;
private Integer code;
private String message;
MsgEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
package yore.utils;
import com.alibaba.fastjson.JSON;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* 上传图片到服务器
*
* Created by yore on 2019/12/7 17:57
*/
public class QiniuUpload {
public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
//密钥配置
private static Auth auth = Auth.create(QiniuConstant.ACCESS_KEY, QiniuConstant.SECRET_KEY);
// 配置对象,设置为华南存储区域
private static Configuration cfg = new Configuration(Zone.huanan());
private static UploadManager uploadManager = new UploadManager(cfg);
//简单上传,使用默认策略,只需要设置上传的空间名就可以了
public static String getUpToken(){
return auth.uploadToken(QiniuConstant.BUCKET);
}
/**
* 将文件上传到 七牛云
*
* @auther: yore
* @param file Multipart对象
* @param nameSpace 上传的文件的命名空间
* @param fileName 新的文件名(为了不重复、去除掉文件中的中文)
* @return String 上传的文件路径
* @date: 2019/12/8 4:23 PM
*/
public static String updateFile(MultipartFile file, String nameSpace, String fileName) throws IOException {
InputStream inputStream=file.getInputStream();
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[600]; //buff用于存放循环读取的临时数据
int rc = 0;
while ((rc = inputStream.read(buff, 0, 100)) > 0) {
swapStream.write(buff, 0, rc);
}
// 给图片命名空间加上时间
nameSpace = nameSpace.endsWith("/")? nameSpace: nameSpace+"/";
nameSpace = nameSpace + sdf.format(new Date()) + "/";
byte[] uploadBytes = swapStream.toByteArray();
try {
Response response = uploadManager.put(uploadBytes, nameSpace + fileName, getUpToken());
//解析上传成功的结果
DefaultPutRet putRet;
putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class);
return QiniuConstant.DOMAIN + "/"+ putRet.key;
} catch (QiniuException ex) {
Response r = ex.response;
System.err.println(r.toString());
try {
System.err.println(r.bodyString());
} catch (QiniuException ex2) {
}
}
return null;
}
/**
* 获取一个随机的新文件名
*
* @auther: yore
* @param fileName 旧文件名
* @return String 新文件名
* @date: 2019/12/8 4:27 PM
*/
public static String getNewFileName(String fileName){
String newName = UUID.randomUUID().toString();
// uuid + 文件后缀
newName = newName + fileName.substring(fileName.lastIndexOf("."), fileName.length());
return newName;
}
}
需要认证的接口方法的注解类 UserLoginToken
package yore.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 需要登录才能进行操作的注解 UserLoginToken
*
* Created by yore on 2019/11/9 10:24 AM
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
package yore.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import yore.annotation.UserLoginToken;
import yore.utils.MsgEnum;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* 通过拦截器判断用户是否登录,
* 添加注解 @UserLoginToken 生效,其它不验证
*
* Created by yore on 2019/12/1 11:40 AM
*/
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
/**
* 预处理回调方法,实现处理器的预处理。
* 主要拦截验证 head 中是否携带 uid
*
* @param request HttpServletRequest
* @param response HttpServletResponse
* @param handler 响应的处理器
* @return boolean 返回值为true表示继续流程(如调用下一个拦截器或处理器)或者接着执行postHandle()和afterCompletion();
* false表示流程中断,不会继续调用其他的拦截器或处理器,中断执行。
* @throws Exception Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从 http 请求头中取出 uid
String uid = request.getHeader("uid");
// 如果不是映射到方法直接通过
if(!(handler instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method method = handlerMethod.getMethod();
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证
if (StringUtils.isEmpty(uid)) {
log.error(MsgEnum.OK_1.getMessage());
throw new RuntimeException(MsgEnum.OK_1.getMessage());
}
//TODO 验证用户 uid 是否合法
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// noop
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// noop
}
}
package yore.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import yore.interceptor.AuthenticationInterceptor;
/**
* Created by yore on 2019/11/10 13:10
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
// 拦截所有请求,通过判断是否有 Tocken 认证的注解 决定是否需要登录
.addPathPatterns("/**");
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
}
package yore.config;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import java.nio.charset.Charset;
/**
* Created by yore on 2019/12/8 19:46
*/
@Configuration
public class MyFastJsonConfig {
@Bean
public HttpMessageConverters /*FastJsonHttpMessageConverter*/ fastJsonHttpMessageConverters(){
//创建FastJson的消息转换器
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
config.setCharset(Charset.forName("UTF-8"));
/**
* JOSN 中输入类名
* 是否输出value 为null 的数据
* 生成的 JSON格式化
* 空集合输出[] 而非 null
* 空字符串输出 "" 而非 null
*/
config.setSerializerFeatures(
SerializerFeature.WriteClassName,
SerializerFeature.WriteMapNullValue,
SerializerFeature.PrettyFormat,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteNullStringAsEmpty
);
converter.setFastJsonConfig(config);
HttpMessageConverter<?> con = converter;
return new HttpMessageConverters(con);
}
}
package yore.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.MultipartConfigElement;
/**
* Created by yore on 2019/12/7 21:18
*/
@Configuration
public class MulterFile {
@Value("${multipart.maxFileSize}")
private String maxFileSize;
@Value("${multipart.maxRequestSize}")
private String maxRequestSize;
/**
* 文件上传配置
* @return MultipartConfigElement
*/
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
//文件最大
factory.setMaxFileSize(maxFileSize); //KB,MB
// 设置总上传数据总大小
factory.setMaxRequestSize(maxRequestSize);
return factory.createMultipartConfig();
}
}
package yore.VO;
import lombok.Data;
/**
* 与前端交互的数据对象。
*
* Created by yore on 2019/12/7 10:57
*/
@Data
public class FileVO implements java.io.Serializable {
private static final long serialVersionUID = 1682073645232877600L;
/** 响应状态 */
private Integer code;
/** 状态信息 */
private String msg;
/** 资源链接 */
private String url ;
/** 图片宽度 */
private Integer width;
/** 图片高度 */
private Integer height;
}
package yore.VO;
import lombok.Data;
/**
*
* Created by yore on 2018-11-30 21:11
*/
@Data
public class ResultVO<T> implements java.io.Serializable{
private static final long serialVersionUID = 4530709418613174547L;
// 响应状态
private Integer code;
// 状态信息
private String msg;
// 响应时间戳
private Long t;
// 数据体
private T data;
public ResultVO() {
this.t = System.currentTimeMillis();
}
}
package yore.entity;
import lombok.Data;
import javax.persistence.*;
/**
* 保存到数据库中的实体对象
*
* Created by yore on 2019/12/7 10:57
*/
@Data
@Entity(name = "file_image")
public class FileImageEntity implements java.io.Serializable {
private static final long serialVersionUID = 5519954279503376340L;
/** 主键,使用自增方式 */
@Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id ;
@Column(name = "uid", length = 12, nullable = false)
private Long uid ;
/** 资源链接 */
@Column
private String url ;
/** 图片宽度 */
private Integer width;
/** 图片高度 */
private Integer height;
}
使用 JPA 方式持久化数据
package yore.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import yore.entity.FileImageEntity;
/**
* Created by yore on 2019-12-7 13:20
*/
@Repository
public interface FileImageRepository extends JpaRepository<FileImageEntity, Long> {
}
服务层接口类
package yore.service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import yore.VO.FileVO;
import yore.entity.FileImageEntity;
import java.util.List;
/**
* 文件的服务层接口
*
* Created by yore on 2019/12/7 18:18
*/
public interface FileService {
/**
* 单文件上传
*
* @auther: yore
* @param file 文件对象
* @return FileVO
* @date: 2019/12/7 7:16 PM
*/
FileVO imageload(MultipartFile file);
/**
* 多文件上传
*
* @auther: yore
* @param multiValueMap 文件Map对象
* @return List
* @date: 2019/12/7 7:19 PM
*/
List<FileVO> imagesload(MultiValueMap<String, MultipartFile> multiValueMap);
/**
* 保存图片实体对象信息
*
* @auther: yore
* @param fileImageEntity
* @date: 2019/12/7 1:56 PM
*/
void saveFileInfo(FileImageEntity fileImageEntity);
}
服务层接口实现类
package yore.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import yore.VO.FileVO;
import yore.entity.FileImageEntity;
import yore.repository.FileImageRepository;
import yore.service.FileService;
import yore.utils.MsgEnum;
import yore.utils.QiniuUpload;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by yore on 2019/12/7 18:20
*/
@Slf4j
@Service
public class FileServiceImpl implements FileService {
@Value("${my.qiniu.image.namespace}")
private String imageNameSpace;
@Autowired
private FileImageRepository fileImageRepository;
@Override
public FileVO imageload(MultipartFile file) {
FileVO fileVO = new FileVO();
try {
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
if(bufferedImage == null){
// 文件非图片
fileVO.setCode(MsgEnum.OK_2.getCode());
fileVO.setMsg(MsgEnum.OK_2.getMessage() + ":" + file.getName());
return fileVO;
}
fileVO.setWidth(bufferedImage.getWidth());
fileVO.setHeight(bufferedImage.getHeight());
bufferedImage = null;
String url = QiniuUpload.updateFile(file, imageNameSpace, QiniuUpload.getNewFileName(file.getOriginalFilename()));
fileVO.setUrl(url);
fileVO.setCode(MsgEnum.SUCCESS_1.getCode());
fileVO.setMsg(MsgEnum.SUCCESS_1.getMessage());
} catch (IOException e) {
log.error(e.getMessage());
fileVO.setCode(MsgEnum.OK_3.getCode());
fileVO.setMsg(MsgEnum.OK_3.getMessage() + ":" + file.getName());
}
return fileVO;
}
@Override
public List<FileVO> imagesload(MultiValueMap<String, MultipartFile> multiValueMap) {
List<FileVO> list = new ArrayList<>();
multiValueMap.forEach((k, v) -> {
v.forEach(f -> {
list.add(imageload(f));
});
});
return list;
}
@Override
public void saveFileInfo(FileImageEntity fileImageEntity) {
if(!StringUtils.isEmpty(fileImageEntity.getUrl())){
fileImageRepository.save(fileImageEntity);
}
}
}
package yore.controller;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import yore.VO.FileVO;
import yore.VO.ResultVO;
import yore.annotation.UserLoginToken;
import yore.entity.FileImageEntity;
import yore.service.FileService;
import yore.utils.MsgEnum;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* Created by yore on 2019/12/7 18:09
*/
@RestController
@RequestMapping("/file")
public class FileController {
@Autowired
private FileService fileService;
/**
* 上传图片的接口
*
* 注意:
* 1、((MultipartHttpServletRequest) request).getFileMap() 会根据 key 只保留单个 MultipartFile
* 2、 如果确定 key 的值,推荐使用 ((MultipartHttpServletRequest) request).getFiles("file")
* 3、保留所有的 MultipartFile 使用 ((MultipartHttpServletRequest) request).getMultiFileMap()
*
*
* @auther: yore
* @param request HttpServletRequest
* @return ResultVO> 上传图片的状态及信息
* @throws Exception
*/
@UserLoginToken
@PostMapping(value = "/upload")
public ResultVO<List<FileVO>> upload(HttpServletRequest request) throws Exception {
//List files = ((MultipartHttpServletRequest) request).getFiles("file");
//Map fileMap = ((MultipartHttpServletRequest) request).getFileMap();
MultiValueMap<String, MultipartFile> multiValueMap = ((MultipartHttpServletRequest) request).getMultiFileMap();
System.out.println(multiValueMap);
ResultVO<List<FileVO>> resultVO = new ResultVO<>();
List<FileVO> listFileVO = fileService.imagesload(multiValueMap);
Long uid = Long.parseLong(request.getHeader("uid"));
listFileVO.forEach(fileVO -> {
FileImageEntity fileImageEntity = new FileImageEntity();
BeanUtils.copyProperties(fileVO, fileImageEntity);
fileImageEntity.setUid(uid);
fileService.saveFileInfo(fileImageEntity);
});
resultVO.setCode(MsgEnum.SUCCESS_1.getCode());
resultVO.setMsg(MsgEnum.SUCCESS_1.getMessage());
resultVO.setData(listFileVO);
return resultVO;
}
}
使用 Postman 测试文件上传接口,POST 方式请求接口 http://localhost:8081/file/upload
,当 headers 中没有 uid
参数时,会返回如下信息,从返回的 JSON 信息可以看到我们注册的 FastJosn 已经生效,返回的 JSON 中包含JOSN 中输入类名"@type"
,同时也有日期"timestamp"
信息且其格式为yyyy-MM-dd HH:mm:ss
{
"@type": "java.util.LinkedHashMap",
"timestamp": "2019-12-07 21:49:51",
"status": 500,
"error": "Internal Server Error",
"message": "请先登录",
"path": "/file/upload"
}
在 postman 工具中,选择 Body -> form-data,KEY类型选择为 File,选择一张中文的图片,请求接口,返回如下
本次上传多个文件,其中一份文件为文本,请求结果如下,会发现非图片的文件提示了错误信息,其它为图片的已经成功上传,并且获取到了图片的宽和高等信息。
然后我们在浏览器上访问图片返回的 url 地址,可以查看的真实的图片。
查看数据库,可以看到图片信息也已经保存。
mysql> use test;
-- id 为主键,uid 用户标识,width 图片宽,height 图片高度,单位像素。
mysql> select * from file_image;
+----+--------+-----+-------------------------------------------------------------------------------------------------+-------+
| id | height | uid | url | width |
+----+--------+-----+-------------------------------------------------------------------------------------------------+-------+
| 7 | 610 | 325 | http://q24zo797k.bkt.clouddn.com/yore/image/2019/12/07/22e8566a-0e4b-451f-a206-99c1bcf8800a.png | 602 |
| 6 | 534 | 325 | http://q24zo797k.bkt.clouddn.com/yore/image/2019/12/07/dc6a8f02-3669-4b2f-af1e-4159c912afea.png | 660 |
+----+--------+-----+-------------------------------------------------------------------------------------------------+-------+
2 rows in set (0.00 sec)
在控制台可以看到上传的文件信息,如果是图片可以很方便的查看到图片的信息。