文件上传下载
-需要直接的路径文件下载(静态资源访问等)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MvcConfig {
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
/**
* 首页设置
* @param registry
*/
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("html/index");
registry.addViewController("/index.html").setViewName("html/index");
}
/**
* 静态资源虚拟地址映射
* 文件上传读取相关
* @param registry
*/
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//获取jar包物理路径
ApplicationHome ah = new ApplicationHome(getClass());
File jarFile = ah.getSource();
String filePath = jarFile.getParentFile().getPath() + "/upload";
System.out.println("初始化文件上传路径:" + filePath);
//添加静态文件资源与实际文件路径之间的映射(app_file换成自定义路径)
registry.addResourceHandler("/app_file/**")
.addResourceLocations("file:" + filePath + "/") ;
}
};
}
}之后我们就可以通过
ip:端口/app_file/文件路径
来访问对应的文件资源了服务器直接读取文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49public class FileService implements InitializingBean{
private static String FILE_PATH;
//这里不能直接在static代码中编写方法,因为getClass()方法会报空指针
//实现InitializingBean的afterPropertiesSet方法,注入FILE_PATH
public void afterPropertiesSet() throws Exception {
//获取jar包物理路径
File jarFile = null;
String filePath = null;
try {
ApplicationHome ah = new ApplicationHome(getClass());
jarFile = ah.getSource();
filePath = jarFile.getParentFile().getPath() + "/upload/";
FILE_PATH = filePath;
System.out.println("初始化文件上传路径:" + filePath);
} catch (NullPointerException e) {
// 运行单元测试时ApplicationHome(getClass())会报空指针异常
}
}
public String getContent(String location) {
StringBuilder builder = new StringBuilder();
location = FILE_PATH + location;
try {
contentFile = ResourceUtils.getFile(location);
} catch (FileNotFoundException e) {
System.out.println("文件不存在,location:" + location);;
}
//流操作使用try-with-resource方式,更安全
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(contentFile))) {
String tmpLine;
int i = 2;
while ((tmpLine = bufferedReader.readLine()) != null) {
if (i > 0) {
if (tmpLine.equals("---"))
i--;
} else if (!tmpLine.equals("<!--more-->")) {
builder.append(tmpLine);
builder.append("\n");
}
}
} catch (IOException e) {
e.printStackTrace();
}
content = builder.toString();
return content;
}
}服务器接收上传文件
直接使用
CommonsMultipartFile.transferTo()
会有打jar包保存文件不兼容问题,这里通过
FileUtils.copyInputStreamToFile()
来进行实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class FIleController {
//最大限制1M
private static final Long FILE_SIZE_LIMIT = 1L * 1024L * 1024L;
private FIleService fIleService;
/**
* 数据和文件必须异步上传
* 上传文件早于保存数据
* @param file
* @param params
* @return
* @throws IOException
*/
public JsonObject upload( MultipartFile file, Map<String, Object> params)throws IOException {
JsonObject res = new JsonObject();
if (!verifyFile(file, res)) {
//验证不通过
return res;
}
String uploadPath = fIleService.upload(file, blogId);
res.put("uploadPath", uploadPath);
return res;
}
/**
* 验证是否通过
* @param res 这里如果检验不通过会在res中设置status和message
* @return 检验结果
*/
private Boolean verifyFile(MultipartFile file, JsonObject res) {
if (file.getSize() > FILE_SIZE_LIMIT) {
res.setStatus(false);
res.setMessage("文件不能超过最大值1M!");
return false;
}
String originName = file.getOriginalFilename();
//这里需要注意转义问题
String[] names = originName.split("\\.");
String lastName = names[names.length - 1];
if (!"md".equals(lastName) && !"markdown".equals(lastName)) {
res.setStatus(false);
res.setMessage("文件类型不正确!请上传markdown文件");
return false;
}
return true;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//FileService中的方法
public String upload(MultipartFile file, String blogId) throws IOException {
Date date = new Date();
int year = date.getYear();
int month = date.getMonth();
//年月子文件夹路径前缀
String datePath = "/" + (1900 + year) + "-" + (1 + month);
File realPath = new File(FILE_PATH + datePath);
if (!realPath.exists()) {
realPath.mkdir();
}
String blogPath = realPath + "/" + blogId;
//保存文件
FileUtils.copyInputStreamToFile(file.getInputStream(), new File(blogPath));
System.out.println("上传文件路径:" + blogPath);
return "public/posts" + datePath + "/" + blogId;
}
Jackson序列化相关
时间转换
虽然jdk8提供了新的日期api,但是一般使用基础的java.util.Date
或者java.util.TimeStamp
就足够了
这里注意不要将java.util.Date和java.sql.Date搞混,后者是前者的子类,toString时只展示日期,同理还有java.util.Timestamp和java.sql.TimeStamp
我在项目中所使用的是java.util.Date
,在格式化日期显示方面,可以选择使用下面方式手动进行转换:
1 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
但这种方式需要在每个调用的位置都手动赋值,并且需要构建新的传输对象VO
这里推荐使用Jackson包含的注解@JsonFormat
来实现
jackson依赖已经由springboot自动引入
在后端服务器响应日期数据时,只需要在实体类的属性上加上
@JsonFormat
,Jackson在序列化时就会自动帮我们完成日期格式转换1
2
3
4
5class Entity {
private Long id;
private Date createTime;
}在接收前端日期数据时使用
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
来接收参数,可以自动转换成Date实体类由于时间一般由服务器自行生成,我在项目中并没有实际用到这个注解
Long和String的转换
众所周知,服务器向前端直接传输Long类型数据时,如果Long数据的长度较长,可能会造成精度丢失
因此一般服务端需要将Long类型转换成String类型进行传输,以防止精度丢失
这里同样推荐使用Jackson的统一配置进行实现
1 |
|
Thymeleaf小坑
在编写Html中的js代码时,如果需要通过[[$value]]
来获取ModelAndView中的数据,需要在对应的script标签上添加th标签,如下所示:
1 | <script type="text/javascript" th:inline="javascript"> |
否则js会直接转义替换string造成页面代码错误
统一Ajax返回对象
这里自定义了用于返回服务器数据的对象传输类,方便统一进行管理
1 | package com.lan5th.blog.utils; |