文件上传,存储至本地目录中 一、代码 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、下载效果