乐趣区

前后分离文件上传

最近在做一个基于 Flutter 的 app,算是学习新的移动端开发技术。于是就需要一个后端 api 接口,其中涉及到了文件上传,特此记录下
1. 为什么自己写文件上传
本来我计划的是,后台只做数据接口,不做文件存储,毕竟自己也没那么多的服务器资源去存储。当时想的是用免费的第三方云存储解决方案,毕竟之前已经用过了七牛云。
但是问题来了,免费的云存储,老是出问题。比如七牛的,过段时间就会发现,外链访问文件会失效,而且后台文件管理面板,察看文件也看不了,很坑。
然后又看了有拍云,和腾讯云,它们提供的文件存储都不收费,但是要后收费,当你的文件存储超过容量时收费。这也还好,毕竟免费的,存储图片的话 5g 也够用了,但是主要是每天的流量由限制,超出流量收费。你后期不得面临你的应用中的图片全部不显示,作为定位上线的应用,是不能接受这种风险的。
所以你如果自己玩,完全可以使用免费的云存储,要是真的商用,就考虑付费产品。但是我又穷,所以只能自己写了。
2 后端开发(使用 SpringBoot)
就不贴全部的项目代码了,只贴上传部分的。因为使用了静态资源访问,所以需要加入模板引擎依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
因为代码比较简单,所以直接 controller 全部处理完:

package com.mike.controller;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.CrossOrigin;
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 com.mike.bean.ApiResult;
import com.mike.bean.FileView;
import com.mike.util.ApiUtils;
import com.mike.util.Tools;

/**
* The class UploadController
*/
@RestController
@RequestMapping(“/up”)
public class UploadController {

@Value(“${baseUrl}”)
private String baaseUrl;

@CrossOrigin
@PostMapping(“/upload”)
public ApiResult uploadFile(@RequestParam(“file”) MultipartFile file){
if (file.isEmpty()) {
return ApiUtils.err(“ 你没有选择上传文件 ”);
} else if(file.getOriginalFilename().contains(“..”)||!file.getOriginalFilename().contains(“.”)){
return ApiUtils.err(“ 文件格式有误 ”);
} else {
String fileName = file.getOriginalFilename();
// 为防止文件名重复覆盖
fileName = fileName.replace(“.”, System.currentTimeMillis()+”.”);
Path savePath = Paths.get(“src/main/resources/static/upload”);
try {
Files.copy(file.getInputStream(), savePath.resolve(fileName), StandardCopyOption.REPLACE_EXISTING);
FileView view = new FileView();
view.setName(fileName);
view.setSize(file.getSize());
view.setUrl(baaseUrl+fileName);
view.setUploadDate(Tools.getCurrent());
return ApiUtils.success(view);
} catch (IOException e) {
e.printStackTrace();
return ApiUtils.err(“ 对不起, 上传失败 ”);
}
}
}
}
在高并发下,还是有非常小的几率出现文件重名,所以使用时间戳也不是好的解决方案。需要能够生成唯一识别符号,建议使用 UUId。
3 前端测试(使用 jquery ajax)
<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8″>
<title></title>
</head>
<body>

<input type=”file” id=”file”>

<button type=”button” onclick=”up()” name=” 上传 ”> 上传 </button>

<script src=”https://cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/lib/jquery/jquery.js”></script>
<script type=”text/javascript”>

function up(){
var formData = new FormData();
formData.append(“file”, $(“#file”)[0].files[0]);
console.log(formData);
$.ajax({
         type:’POST’,
         url:”http://localhost:8080/up/upload”,
         data:formData,
         contentType:false,
         processData:false,// 这个很有必要,不然不行
         dataType:”json”,
         mimeType:”multipart/form-data”,
         success:function(data){
                if(“00″==data.code){
                    console.log(data.data);
                }else{
                    console.log(“error”);
                }
            }
     });

}
</script>
</body>
</html>
效果:
访问上传后的图片 url:

4 填坑
因为图片上传导了代码目录,不是服务器的目录,所以,新上传得图片访问会报 404,需要重起才能访问。为了解决这个问题,我们需要增加文件与路径的映射关系,这样,就不会出现 404
public class FileConfig implements WebMvcConfigurer {

@Value(“${serverFilePath}”)
private String serverFilePath;

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
registry.addResourceHandler(“/upload/**”)
.addResourceLocations(serverFilePath);
}
}
serverFilePath 在 properties 中配置:
baseUrl=http://localhost:8080/upload/
serverFilePath=file:C:/code/mike-todo/src/main/resources/static/upload/
上线的话,换成服务器上的路径就好。
5 总结
文件存储,远远不像我写的这么简单。只是应付一般小项目足够了,如果是大型的项目,就需要专门的文件存储系统以及服务器端的优化。如果你有什么问题,可以留言要论。或是关注我的公众号:mike 啥都想搞,还有其他教学视频免费领取。

退出移动版