一、文件的上传与获取的流程:
先来看下项目各部分在文件操作过程中各自的职责:
部分 | 职责 |
前端 | 数据上传的入口;文件展示的窗口 |
后端 | 上传后文件在生成记录(文件id)并在数据库中写入记录;通过数据库中的记录(文件id)从文件存储服务器中拿到文件 |
数据库 | 存储文件的记录(文件id)或者文件路径 |
文件存储服务器 | 在本地的话其实就是本地文件夹,用于存储文件实体 |
1.1 文件的上传流程(以下图中数字编号表示执行顺序)
1)文件有所属实体的情况:
*注:所属实体,指的就是我们要操作的文件其实是某一个实体的一个属性,比如车辆实体,假设每一个车辆实体都有一张图片文件,那这些图片文件的所属实体就是车辆实体,再通俗一点来说就是文件名在数据库表中作为一个字段。
1. 从前端上传文件,将所属于文件实体的主键一并传给后端;
2. 将文件存入文件存储服务器中;
3. 后端根据一定的算法生成文件的专属唯一id(用于后续获取文件),并将文件唯一id存入数据库表中主键对应数据条中;
4. 返回给前端状态码(自己定义,上传成功、失败或者其他错误)。
或是下面的流程:
1)文件无所属实体,即直接存储的情况(少见):
1.2 文件的查找流程
1)文件有所属实体的情况:
首先,这一步默认已经经过了上一步的上传操作,即数据库表中相应数据条有文件名字段,同样文件存储服务器(本地文件夹)中有相应文件数据:
1. 前端将要查找的文件名所在的实体的主键传入后端,比如汽车图片,要将图片所属于的汽车的id传入后端,这样后端就知道要读取哪一个id的汽车的图片的文件名。
2. 后端将前端传入的主键在后端数据库中进行查找
3. 数据库中返回相应主键的文件的文件名;
4.后端将文件名拿到之后到相应的文件存储服务器(本地文件夹)中进行获取
5. 通过文件名,后端经过逻辑处理,取到文件的输出流(字节码文件),即文件实体从文件存储服务器传给后端;
6. 后端将拿到的文件实体返回给前端进行输出。
或是下面的流程:
2)文件无所属实体,即通过文件名直接获取的情况:
上面1)中的步骤只是一种方案,还有其他的方式进行文件的获取,比如前端可以直接通过文件名进行获取,也是可以的:
二、实际项目中代码:
这里主要分享下后端的代码:
2.1 文件的上传方法:
@PostMapping(value = "/uploadFile")
public UploadFileDTO uploadFile(@RequestParam(value = "file", required = true) MultipartFile file){
//定义返回上传文件UploadFileDTO对象
UploadFileDTO uploadFileDTO = new UploadFileDTO();
//定义上传文件路径(绝对路径),即最后在服务器端的文件存储路径
String upLoadFilePath = ResourceUtils.getURL("classpath:").getPath() + "static/images/upload/";
File targetFile = new File(upLoadFilePath);
if (!targetFile.exists()) {
targetFile.mkdirs();
}
String filename = file.getOriginalFilename();
Long fileSize = file.getSize();
String fileType = file.getContentType();
// 生成唯一的文件名ID
String fileNameUnique = this.generateFileNameIdByFileName(filename);
// 相对路径
String relativePath = "/images/upload/"+fileNameUnique;
FileOutputStream out = new FileOutputStream(upLoadFilePath + fileNameUnique);
out.write(file.getBytes());
//上传目录路径
uploadFileDTO.setUpLoadFilePath(upLoadFilePath);
//相对路径
uploadFileDTO.setRelativePath(relativePath);
//原始文件名
uploadFileDTO.setOriginalFilename(filename);
//加密文件名
uploadFileDTO.setFileNameUnique(fileNameUnique);
//文件类型
uploadFileDTO.setFileType(fileType);
//文件大小
uploadFileDTO.setFileSize(fileSize);
//设置返回数据
return UploadFileDTO;
}
通过原始文件名生成唯一文件id的方法:
/**
* @param fileName 原始文件名
* @return 原始文件名的MD5码值
*/
private String generateFileNameIdByFileName(String fileName) {
//文件后缀(比如: jpg 或者 gif 等等)
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
//文件名(比如: 山水画 等等)
String prefixFileName = fileName.substring(0,fileName.lastIndexOf("."));
StringBuilder buffer = new StringBuilder();
long currentTimeMills = System.currentTimeMillis();
buffer.append(prefixFileName).append(String.valueOf(currentTimeMills))
.append(RandomUtil.getRandomString(10));
String uniqueFileName = MD5Util.getMD5(buffer.toString()).concat(".").concat(suffix).toLowerCase();
return uniqueFileName;
}
上面的方法只是一种思路,具体可以根据业务以及技术条件与技术规范进行封装、改写,比如可以写try()catch()块将逻辑语句包起来,如果文件上传不成功可以报错等等,上面的唯一文件名生成方法也可以根据业务量以及技术要求进行改写,可以用其他的算法进行生成等等。
2.2 文件的获取方法:
@RequestMapping(value = "/getCodeFile",method = RequestMethod.GET)
public void getFile(HttpServletRequest request , HttpServletResponse response, String fileName) throws IOException {
OutputStream outputStream= null;
InputStream in = null;
try{
//读取路径下面的文件
File file = new File("/项目的绝对路径(含项目名)/target/classes/static/images/upload/" + fileName);
//获取文件后缀名格式
String ext = file.getName().substring((file.getName().indexOf(".")));
//判断图片格式,设置相应的输出文件格式
if(ext.equals("jpg")){
response.setContentType("image/jpeg");
}else if(ext.equals("JPG")){
response.setContentType("image/jpeg");
}else if(ext.equals("png")){
response.setContentType("image/png");
}else if(ext.equals("PNG")){
response.setContentType("image/png");
}
//读取指定路径下面的文件
in = new FileInputStream(file);
outputStream = new BufferedOutputStream(response.getOutputStream());
//创建存放文件内容的数组
byte[] buff =new byte[1024];
//所读取的内容使用n来接收
int n;
//当没有读取完时,继续读取,循环
while((n=in.read(buff))!=-1){
//将字节数组的数据全部写入到输出流中
outputStream.write(buff,0,n);
}
//强制将缓存区的数据进行输出
outputStream.flush();
//关流
outputStream.close();
in.close();
}catch (Exception e){
e.printStackTrace();
}finally {
if(outputStream!=null){
outputStream.close();
}
if(in!=null){
in.close();
}
}
}
(*本博客仅供思路参考,如有错误请指正)