项目实训11
1. 背景
由于项目中有需要上传图片的需求,所以在此记录一下上传图片的流程和做法
2. 做法
本需求的流程是,前端向后端上传图片,后端接受图片,并返回图片的打分。
@UserLoginToken
@ResponseBody
@PostMapping("/upload")
public Result uploadPicture(@RequestParam("file") MultipartFile file,
int id,
HttpServletRequest request) throws IOException, InterruptedException
{
if (file == null)
throw new CustomException("图片文件为空");
String openid = openidUtils.getOpenidFromRequest(request);
// 根据源图片类型创建响应的文件夹
String path = base_path;
path += Integer.toString(id);
path += "/";
//将文件保存到服务器指定位置
try
{
//获取文件在服务器的储存位置
File filePath = new File(path);
log.debug("文件的保存路径" + path);
if (!filePath.exists() && !filePath.isDirectory())
{
log.debug("目录不存在,创建目录" + filePath);
filePath.mkdir();
}
Upload uploadImage = new Upload();
//获取原始文件名称(包括格式)
String originalFileName = file.getOriginalFilename();
//获取文件类型,以最后一个‘.’为标识
String type = originalFileName.substring(originalFileName.lastIndexOf(".") + 1);
// 以用户的openid对图片进行命名
String name = openid;
// 设置文件新名称:用户的openid
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String date = sdf.format(d);
String fileName = date + name + "." + type;
// 在指定路径下创建文件
File targetFile = new File(path, fileName);
file.transferTo(targetFile);
System.out.println("上传成功");
// 保存upload的信息到数据库
uploadImage.setName(id);
uploadImage.setAddress(path + fileName);
uploadImage.setTime(d);
uploadImage.setOpenid(openid);
// TODO: 这里还有一个需要做的工作是调用python脚本获取评分 (根据图片的本地地址) 最后将评分返回给前端,也得保存至数据库
// 由于获取到的数据为string类型,所以这边先转为double
// 然后得分 *100
// 然后再转化为整数
String result = this.getScore(path + fileName, id);
double res = Double.parseDouble(result) * 100;
int score = (int) res;
uploadImage.setScore(score);
// 更新source表相应图片的描绘次数
sourceService.updateCountById(id);
// 更新user表的总评分
userService.updateUserScore(uploadImage.getScore(), openid);
// 插入upload表信息
uploadService.insertUpload(uploadImage);
Map<String, Object> map = new HashMap<>();
map.put("score", score);
return ResultFactory.success().message("上传成功").data(map);
} catch (Exception e)
{
// System.out.println("上传失败");
// result.put("code",400);
log.error("保存图片失败");
e.printStackTrace();
return ResultFactory.error().message("上传失败");
}
}
其中@UserLoginToken注解已经在前面的博客中有提到,是用来验证用户token的
其中的getOpenidFromRequest方法如下:由于经常要从请求的header中抽出openid,所以剥离出来了一个工具类的方法,主要作用就是从header的token字段中提取出用户的openid。
public String getOpenidFromRequest(HttpServletRequest request)
{
String token = request.getHeader("token");// 从 http 请求头中取出 token
// if(token == null)
// throw new CustomException(ResultCodeEnum.NO_TOKEN);
// 获取 token 中的 user openid
String openid;
try
{
openid = JWT.decode(token).getAudience().get(0);
log.info(openid);
} catch (JWTDecodeException j)
{
throw new RuntimeException("token解析出错");
}
return openid;
}
之后将获取到的图片保存到服务器的某个位置,本项目是保存到了nginx的静态资源配置目录下面,便于后面拓展需求的时候需要返回上传上来的图片给用户,便可以直接返回在线地址。
方法的具体流程在代码的注释已经给的很详细了,就不再阐述了。
上传文件使用的是MultipartFile类。
下面看看MultipartFile工具类的源码
package org.springframework.web.multipart;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.springframework.core.io.InputStreamSource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.util.FileCopyUtils;
public interface MultipartFile extends InputStreamSource {
//getName() 返回参数的名称
String getName();
//获取源文件的昵称
@Nullable
String getOriginalFilename();
//getContentType() 返回文件的内容类型
@Nullable
String getContentType();
//isEmpty() 判断是否为空,或者上传的文件是否有内容
boolean isEmpty();
//getSize() 返回文件大小 以字节为单位
long getSize();
//getBytes() 将文件内容转化成一个byte[] 返回
byte[] getBytes() throws IOException;
//getInputStream() 返回InputStream读取文件的内容
InputStream getInputStream() throws IOException;
default Resource getResource() {
return new MultipartFileResource(this);
}
//transferTo(File dest) 用来把 MultipartFile 转换换成 File
void transferTo(File var1) throws IOException, IllegalStateException;
default void transferTo(Path dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));
}
}
1、“流”是一个抽象的概念,它是对输入输出设备的一种抽象理解,在java中,对数据的输入输出操作都是以“流”的方式进行的。
2、“流”具有方向性,输入流、输出流是相对的。当程序需要从数据源中读入数据的时候就会开启一个输入流,相反,写出数据到某个数据源目的地的时候也会开启一个输出流。
3、数据源可以是文件、内存或者网络等。
所以使用该工具类能够比较便捷地上传文件。