Skip to content

Commit ecd38ff

Browse files
1.2.1-RELEASE 上传下载,静态资源访问 💥
Former-commit-id: b03409ce1815d1450d93cf87f7b25c397ddcf302
1 parent 5e89ca0 commit ecd38ff

File tree

8 files changed

+323
-63
lines changed

8 files changed

+323
-63
lines changed

CHANGELOG.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
# 更新日志 CHANGELOG
22

3-
## [V1.2.1-RELEASE] 2019.08.20
4-
5-
### ⚡️ Optimization
6-
- 启用项目静态资源访问,可访问static/templates目录下资源
7-
- static资源访问:http://127.0.0.1:8888/static/welcome.html
8-
- templates资源访问:http://127.0.0.1:8888/templates/springbootplus.html
3+
## [V1.2.1-RELEASE] 2019.08.21
94

105
### ⭐️ New Features
116
- 文件上传保存到服务器指定目录
127
- 文件下载
138
- 访问上传的图片等资源
14-
- static/templates项目静态资源访问
9+
- 启用项目静态资源访问,可访问static/templates目录下资源
10+
11+
### ⚡️ Optimization
12+
- static资源访问:[http://localhost:8888/static/welcome.html](http://localhost:8888/static/welcome.html)
13+
- templates资源访问:[http://localhost:8888/templates/springbootplus.html](http://localhost:8888/templates/springbootplus.html)
14+
- 上传swagger:[http://localhost:8888/swagger-ui.html#!/upload-controller/uploadUsingPOST](http://localhost:8888/swagger-ui.html#!/upload-controller/uploadUsingPOST)
15+
- 上传后,图片文件访问:[http://localhost:8888//resource/201908210134467.png](http://localhost:8888//resource/201908210134467.png)
16+
- 图片自定义控制访问:[http://localhost:8888/image/201908210134467.png](http://localhost:8888/image/201908210134467.png)
1517

1618
### 📝 Added/Modified
1719
- Add `UploadController` 上传控制器
@@ -21,6 +23,8 @@
2123

2224
- Add `welcome.html``static`目录下
2325
- Add `springbootplus.html``templates`目录下
26+
- Add `ContentTypeUtil` 文件类型工具
27+
- Add `mime-type.properties` 文件类型自定义拓展配置
2428

2529
- Modify `WebMvcConfig` 注册资源拦截器,项目静态资源访问配置
2630
- Modify `SpringBootPlusConfig` 创建 `ResourceInterceptor` 资源拦截器
@@ -30,6 +34,11 @@
3034

3135
- Modify `mysql_spring_boot_plus.sql` 添加创建数据库语句,如果不存在,则创建
3236

37+
### 🐞 Bug Fixes
38+
- 拦截器`exclude-path`,`include-path`字符串配置问题,已修改为数组接收`String[] excludePath`,`String[] includePath`
39+
40+
### 📔 Documentation
41+
-[https://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?revision=1752884&view=co](https://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?revision=1752884&view=co)
3342

3443
### 🔨 Dependency Upgrades
3544
- Upgrade to springboot 2.1.7.RELEASE

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@
286286
<include>application-${profileActive}.yml</include>
287287
<include>banner.txt</include>
288288
<include>logback.xml</include>
289+
<include>*.properties</include>
289290
</includes>
290291
</resource>
291292

src/main/java/io/geekidea/springbootplus/upload/web/DownloadController.java

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,15 @@
1818

1919
import io.geekidea.springbootplus.common.api.ApiResult;
2020
import io.geekidea.springbootplus.config.core.SpringBootPlusProperties;
21-
import io.geekidea.springbootplus.util.HttpServletResponseUtil;
21+
import io.geekidea.springbootplus.util.DownloadUtil;
2222
import io.swagger.annotations.ApiOperation;
2323
import lombok.extern.slf4j.Slf4j;
24-
import org.apache.commons.io.FileUtils;
2524
import org.springframework.beans.factory.annotation.Autowired;
2625
import org.springframework.stereotype.Controller;
27-
import org.springframework.web.bind.annotation.*;
26+
import org.springframework.web.bind.annotation.PathVariable;
27+
import org.springframework.web.bind.annotation.RequestMapping;
2828

2929
import javax.servlet.http.HttpServletResponse;
30-
import java.io.BufferedOutputStream;
31-
import java.io.File;
32-
import java.io.OutputStream;
3330

3431
/**
3532
* 下载控制器
@@ -51,30 +48,7 @@ public class DownloadController {
5148
@RequestMapping("/{downloadFileName}")
5249
@ApiOperation(value = "下载文件",notes = "下载文件",response = ApiResult.class)
5350
public void download(@PathVariable(required = true) String downloadFileName, HttpServletResponse response) throws Exception{
54-
log.info("downloadFileName:{}",downloadFileName);
55-
56-
// 从服务器读取文件,然后输出
57-
File file = new File(springBootPlusProperties.getUploadPath(),downloadFileName);
58-
if (!file.exists()){
59-
HttpServletResponseUtil.printJSON(response,ApiResult.fail("文件不存在"));
60-
return;
61-
}
62-
63-
// 下载文件
64-
byte[] bytes = FileUtils.readFileToByteArray(file);
65-
66-
response.reset();
67-
response.setHeader("Content-Disposition", "attachment;fileName=" + downloadFileName);
68-
// TODO 可判断文件类型,输出对应ContentType
69-
// response.setContentType(contentType);
70-
response.addHeader("Content-Length", "" + bytes.length);
71-
72-
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
73-
outputStream.write(bytes);
74-
outputStream.flush();
75-
outputStream.close();
76-
77-
51+
DownloadUtil.download(springBootPlusProperties.getUploadPath(),downloadFileName,response);
7852
}
7953

8054
}

src/main/java/io/geekidea/springbootplus/upload/web/UploadController.java

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@
1818

1919
import io.geekidea.springbootplus.common.api.ApiResult;
2020
import io.geekidea.springbootplus.config.core.SpringBootPlusProperties;
21+
import io.geekidea.springbootplus.util.UploadUtil;
2122
import io.swagger.annotations.ApiOperation;
2223
import lombok.extern.slf4j.Slf4j;
23-
import org.apache.commons.io.FileUtils;
2424
import org.apache.commons.io.FilenameUtils;
2525
import org.springframework.beans.factory.annotation.Autowired;
26-
import org.springframework.web.bind.annotation.*;
26+
import org.springframework.web.bind.annotation.PostMapping;
27+
import org.springframework.web.bind.annotation.RequestMapping;
28+
import org.springframework.web.bind.annotation.RequestParam;
29+
import org.springframework.web.bind.annotation.RestController;
2730
import org.springframework.web.multipart.MultipartFile;
2831

29-
import java.io.File;
30-
import java.io.InputStream;
3132
import java.time.LocalDateTime;
3233
import java.time.format.DateTimeFormatter;
3334

@@ -57,28 +58,15 @@ public ApiResult<Boolean> upload(@RequestParam("img") MultipartFile multipartFil
5758
log.info("Name = " + multipartFile.getName());
5859
log.info("Size = " + multipartFile.getSize());
5960

60-
InputStream inputStream = multipartFile.getInputStream();
61-
// 文件保存目录
62-
File saveDir = new File(springBootPlusProperties.getUploadPath());
63-
// 判断目录是否存在,不存在,则创建,如创建失败,则抛出异常
64-
if (!saveDir.exists()){
65-
boolean flag = saveDir.mkdirs();
66-
if (!flag){
67-
throw new RuntimeException("创建" +saveDir + "目录失败!");
68-
}
69-
}
70-
String srcFileName = multipartFile.getOriginalFilename();
71-
String fileExtension= FilenameUtils.getExtension(srcFileName);
72-
// 这里可自定义文件名称,比如按照业务类型/文件格式/日期
73-
// 此处按照文件日期存储
74-
String dateString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssS"));
75-
log.info("dateString = " + dateString);
76-
77-
String saveFileName = dateString + "." +fileExtension;
78-
log.info("saveFileName = " + saveFileName);
79-
File saveFile = new File(saveDir,saveFileName);
80-
// 保存文件到服务器指定路径
81-
FileUtils.copyToFile(inputStream,saveFile);
61+
// 上传文件,返回保存的文件名称
62+
String saveFileName = UploadUtil.upload(springBootPlusProperties.getUploadPath(), multipartFile, originalFilename -> {
63+
// 文件后缀
64+
String fileExtension= FilenameUtils.getExtension(originalFilename);
65+
// 这里可自定义文件名称,比如按照业务类型/文件格式/日期
66+
String dateString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssS"));
67+
String fileName = dateString + "." +fileExtension;
68+
return fileName;
69+
});
8270

8371
// 上传成功之后,返回访问路径,请根据实际情况设置
8472

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package io.geekidea.springbootplus.util;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.apache.commons.io.FilenameUtils;
5+
import org.apache.commons.lang3.StringUtils;
6+
import org.springframework.core.io.ClassPathResource;
7+
import org.springframework.core.io.support.PropertiesLoaderUtils;
8+
9+
import java.io.File;
10+
import java.io.IOException;
11+
import java.nio.file.Files;
12+
import java.nio.file.Path;
13+
import java.nio.file.Paths;
14+
import java.util.Properties;
15+
16+
/**
17+
* 获取文件的内容类型
18+
* mime-type参考:https://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?revision=1752884&view=co
19+
* @author geekidea
20+
* @date 2019/08/20
21+
* @since
22+
*/
23+
@Slf4j
24+
public final class ContentTypeUtil {
25+
26+
private static String MIME_TYPE_CONFIG_FILE = "config/mime-type.properties";
27+
28+
private static String DEFAULT_MIME_TYPE = "application/octet-stream";
29+
30+
private static Properties properties;
31+
32+
static {
33+
try {
34+
properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(MIME_TYPE_CONFIG_FILE));
35+
} catch (IOException e) {
36+
log.error("读取配置文件" + MIME_TYPE_CONFIG_FILE + "异常",e);
37+
}
38+
log.info(MIME_TYPE_CONFIG_FILE + " = " + properties);
39+
}
40+
41+
/**
42+
* 获取文件内容类型
43+
* @param file
44+
* @return
45+
*/
46+
public static String getContentType(File file){
47+
if (file == null){
48+
return null;
49+
}
50+
Path path = Paths.get(file.toURI());
51+
if (path == null){
52+
return null;
53+
}
54+
String contentType = null;
55+
try {
56+
contentType = Files.probeContentType(path);
57+
} catch (IOException e) {
58+
log.error("获取文件ContentType异常",e);
59+
}
60+
if (contentType == null){
61+
// 读取拓展的自定义配置
62+
contentType = getContentTypeByExtension(file);
63+
}
64+
// 设置默认的内容类型
65+
if (contentType == null){
66+
contentType = DEFAULT_MIME_TYPE;
67+
}
68+
return contentType;
69+
}
70+
71+
/**
72+
* 根据文件后缀获取自定义配置的文件mime-type
73+
* @param file
74+
* @return
75+
*/
76+
private static String getContentTypeByExtension(File file){
77+
if (properties == null){
78+
return null;
79+
}
80+
String extension = FilenameUtils.getExtension(file.getName());
81+
if (StringUtils.isBlank(extension)){
82+
return null;
83+
}
84+
String contentType = properties.getProperty(extension);
85+
return contentType;
86+
}
87+
88+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.geekidea.springbootplus.util;
18+
19+
import lombok.extern.slf4j.Slf4j;
20+
import org.apache.commons.lang3.StringUtils;
21+
import org.springframework.util.Base64Utils;
22+
import org.springframework.util.FileCopyUtils;
23+
24+
import javax.servlet.http.HttpServletRequest;
25+
import javax.servlet.http.HttpServletResponse;
26+
import java.io.*;
27+
import java.net.URLEncoder;
28+
29+
/**
30+
* 文件下载工具类
31+
* @author geekidea
32+
* @date 2019/8/21
33+
* @since 1.2.1-RELEASE
34+
*/
35+
@Slf4j
36+
public final class DownloadUtil {
37+
38+
/**
39+
* 下载文件
40+
* @param downloadDir 文件目录
41+
* @param downloadFileName 文件名称
42+
* @throws Exception
43+
*/
44+
public static void download( String downloadDir, String downloadFileName,HttpServletResponse response) throws Exception {
45+
log.info("downloadDir:{}",downloadDir);
46+
log.info("downloadFileName:{}",downloadFileName);
47+
48+
if (StringUtils.isBlank(downloadDir)){
49+
throw new IOException("文件目录不能为空");
50+
}
51+
if (StringUtils.isBlank(downloadFileName)){
52+
throw new IOException("文件名称不能为空");
53+
}
54+
55+
// 从服务器读取文件,然后输出
56+
File downloadFile = new File(downloadDir,downloadFileName);
57+
if (!downloadFile.exists()){
58+
throw new IOException("文件不存在");
59+
}
60+
61+
// 判断文件类型,输出对应ContentType,如果没有对应的内容类型,可在config/mime-type.properties配置
62+
String contentType = ContentTypeUtil.getContentType(downloadFile);
63+
log.info("contentType:{}",contentType);
64+
// 文件大小
65+
long length = downloadFile.length();
66+
log.info("length:{}",length);
67+
68+
// 下载文件名称编码,Firefox中文乱码处理
69+
String encodeDownFileName;
70+
71+
HttpServletRequest request = HttpServletRequestUtil.getRequest();
72+
73+
String browser = BrowserUtil.getCurrent(request);
74+
if(BrowserUtil.FIREFOX.equals(browser)) {
75+
encodeDownFileName = "=?UTF-8?B?" + (new String(Base64Utils.encodeToString(downloadFileName.getBytes("UTF-8")))) + "?=";
76+
} else {
77+
encodeDownFileName = URLEncoder.encode(downloadFileName,"utf-8").replaceAll("\\+", "%20");
78+
}
79+
log.info("encodeDownFileName:{}",encodeDownFileName);
80+
81+
response.reset();
82+
// 设置Content-Disposition响应头
83+
response.setHeader("Content-Disposition", "attachment;fileName=\"" + encodeDownFileName + "\"");
84+
// 设置响应Content-Type
85+
response.setContentType(contentType);
86+
// 设置响应文件大小
87+
response.setContentLengthLong(length);
88+
89+
// 文件下载
90+
InputStream in = new BufferedInputStream(new FileInputStream(downloadFile));
91+
FileCopyUtils.copy(in, response.getOutputStream());
92+
}
93+
}

0 commit comments

Comments
 (0)