文件上传,存储至本地目录中
- 一、代码
- 1、工具类(敏感后缀过滤)
- 2、文件上传,存储至本地
- 3、文件下载
- 二、效果演示
-
一、代码
1、工具类(敏感后缀过滤)
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
public class FileTypeFilter {
private static String[] forbidType = {"jsp","php"};
final static HashMap<String, String> FILE_TYPE_MAP = new HashMap<>();
static {
FILE_TYPE_MAP.put("3c25402070616765206c", "jsp");
FILE_TYPE_MAP.put("3c3f7068700a0a2f2a2a0a202a205048", "php");
}
private static String getFileTypeBySuffix(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
}
public static void fileTypeFilter(MultipartFile file) throws Exception {
String suffix = getFileType(file);
for (String type : forbidType) {
if (type.contains(suffix)) {
throw new Exception("上传失败,非法文件类型:" + suffix);
}
}
}
private static String getFileType(MultipartFile file) throws Exception {
String fileExtendName = null;
InputStream is;
try {
is = file.getInputStream();
byte[] b = new byte[10];
is.read(b, 0, b.length);
String fileTypeHex = String.valueOf(bytesToHexString(b));
Iterator<String> keyIter = FILE_TYPE_MAP.keySet().iterator();
while (keyIter.hasNext()) {
String key = keyIter.next();
if (key.toLowerCase().startsWith(fileTypeHex.toLowerCase().substring(0, 5))
|| fileTypeHex.toLowerCase().substring(0, 5).startsWith(key.toLowerCase())) {
fileExtendName = FILE_TYPE_MAP.get(key);
break;
}
}
if (StringUtils.isBlank(fileExtendName)) {
String fileName = file.getOriginalFilename();
return getFileTypeBySuffix(fileName);
}
is.close();
return fileExtendName;
} catch (Exception exception) {
throw new Exception(exception.getMessage(), exception);
}
}
private static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder();
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
}
2、文件上传,存储至本地
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.buf.HexUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
@Slf4j
@RestController
@RequestMapping("/file")
public class FileUploadController {
private static String FILE_NAME_REGEX = "[^A-Za-z\\.\\(\\)\\-()\\_0-9\\u4e00-\\u9fa5]";
@Transactional
@PostMapping(value = "/upload")
public String upload(HttpServletRequest request) {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile file = multipartRequest.getFile("file");
if (file == null) {
throw new NullPointerException("请选择文件上传!");
}
String fileName = file.getOriginalFilename();
try {
FileTypeFilter.fileTypeFilter(file);
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] digest = md5.digest(file.getBytes());
String fileMd5 = HexUtils.toHexString(digest);
if (fileMd5.equals("xxxxxxxx")) {
}
int begin = fileName.lastIndexOf(".");
String fileSuffix = fileName.substring(begin + 1);
log.info("文件名称:{}", fileName);
log.info("文件大小:{}", file.getBytes().length);
log.info("文件类型:{}", file.getContentType());
log.info("文件md5 算法:{}", fileMd5);
log.info("文件后缀:{}", fileSuffix);
String filePath = "/opt/test";
File upFile = uploadLocal(file, filePath);
if (upFile == null) {
return "上传存储失败";
}
String savePath = upFile.getPath().replace("\\", "/");
log.info("【存储】文件目录:{}", upFile.getName());
log.info("【存储】文件路径:{}", savePath);
return "成功";
} catch (Exception e) {
return "失败";
}
}
private File uploadLocal(MultipartFile mf, String bizPath) {
String toDay = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
try {
String ctxPath = "";
String fileName;
String fileDir = ctxPath + File.separator + bizPath + File.separator + toDay;
File file = new File(fileDir);
if (!file.exists()) {
file.mkdirs();
}
log.info("上传目录:{}", file.getPath());
String orgName = mf.getOriginalFilename();
if (!StringUtils.hasText(orgName)) {
throw new NullPointerException("请选择文件上传!");
}
orgName = getFileName(orgName);
if (orgName.contains(".")) {
fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
} else {
fileName = orgName + "_" + System.currentTimeMillis();
}
String savePath = file.getPath() + File.separator + fileName;
File saveFile = new File(savePath);
FileCopyUtils.copy(mf.getBytes(), saveFile);
return saveFile;
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return null;
}
public static String getFileName(String fileName) {
int unixSep = fileName.lastIndexOf('/');
int winSep = fileName.lastIndexOf('\\');
int pos = (winSep > unixSep ? winSep : unixSep);
if (pos != -1) {
fileName = fileName.substring(pos + 1);
}
fileName = fileName.replace("=", "").replace(",", "").replace("&", "")
.replace("#", "").replace("“", "").replace("”", "");
fileName = fileName.replaceAll("\\s", "");
fileName = fileName.replaceAll(FILE_NAME_REGEX, "");
return fileName;
}
}
3、文件下载
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
@Slf4j
@RestController
@RequestMapping("/file")
public class FileDownloadController {
@GetMapping(value = "/download")
public void download(String loadType, HttpServletResponse response) {
// 文件路径
String filePath = "/opt/test/xxxx-xx-xx";
// 文件名称
String fileName = "1 - 副本.JPG";
// 文件类型
String contentType = "image/png";
// 文件大小
Integer fileSize = 17408;
fileLoad(response, filePath, fileName, contentType, fileSize, loadType);
}
public void fileLoad(HttpServletResponse response, String filePath, String fileName, String contentType, Integer fileSize, String loadType) {
File file = new File(filePath);
if (!file.exists()) {
response.setStatus(404);
throw new RuntimeException("文件[" + fileName + "]不存在..");
}
InputStream inputStream = null;
OutputStream outputStream = null;
// 其余处理略
try {
inputStream = new BufferedInputStream(new FileInputStream(filePath));
log.info("下载文件,{},大小:{}.kb", fileName, fileSize / 1024);
// 设置强制下载不打开
response.setContentType(contentType);
String dis = "1".equals(loadType) ? "attachment" : "inline";
response.addHeader("Content-Disposition", dis + ";fileName=" + URLEncoder.encode(fileName, "UTF-8"));
response.addHeader("content-length", String.valueOf(fileSize));
outputStream = response.getOutputStream();
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, len);
}
response.flushBuffer();
} catch (IOException e) {
log.error("读取文件失败" + e.getMessage());
response.setStatus(404);
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
}
}
二、效果演示
1、上传
1.1、postMan 请求
1.2、上传效果
2、下载
2.1、下载效果