正在显示
3 个修改的文件
包含
130 行增加
和
13 行删除
| @@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletResponse; | @@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletResponse; | ||
| 5 | import org.slf4j.Logger; | 5 | import org.slf4j.Logger; |
| 6 | import org.slf4j.LoggerFactory; | 6 | import org.slf4j.LoggerFactory; |
| 7 | import org.springframework.beans.factory.annotation.Autowired; | 7 | import org.springframework.beans.factory.annotation.Autowired; |
| 8 | +import org.springframework.http.MediaType; | ||
| 8 | import org.springframework.web.bind.annotation.GetMapping; | 9 | import org.springframework.web.bind.annotation.GetMapping; |
| 9 | import org.springframework.web.bind.annotation.PostMapping; | 10 | import org.springframework.web.bind.annotation.PostMapping; |
| 10 | import org.springframework.web.bind.annotation.RestController; | 11 | import org.springframework.web.bind.annotation.RestController; |
| @@ -41,17 +42,15 @@ public class CommonController | @@ -41,17 +42,15 @@ public class CommonController | ||
| 41 | { | 42 | { |
| 42 | try | 43 | try |
| 43 | { | 44 | { |
| 44 | - if (!FileUtils.isValidFilename(fileName)) | 45 | + if (!FileUtils.checkAllowDownload(fileName)) |
| 45 | { | 46 | { |
| 46 | throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); | 47 | throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); |
| 47 | } | 48 | } |
| 48 | String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); | 49 | String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); |
| 49 | String filePath = RuoYiConfig.getDownloadPath() + fileName; | 50 | String filePath = RuoYiConfig.getDownloadPath() + fileName; |
| 50 | 51 | ||
| 51 | - response.setCharacterEncoding("utf-8"); | ||
| 52 | - response.setContentType("multipart/form-data"); | ||
| 53 | - response.setHeader("Content-Disposition", | ||
| 54 | - "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, realFileName)); | 52 | + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); |
| 53 | + FileUtils.setAttachmentResponseHeader(response, realFileName); | ||
| 55 | FileUtils.writeBytes(filePath, response.getOutputStream()); | 54 | FileUtils.writeBytes(filePath, response.getOutputStream()); |
| 56 | if (delete) | 55 | if (delete) |
| 57 | { | 56 | { |
| @@ -92,18 +91,28 @@ public class CommonController | @@ -92,18 +91,28 @@ public class CommonController | ||
| 92 | * 本地资源通用下载 | 91 | * 本地资源通用下载 |
| 93 | */ | 92 | */ |
| 94 | @GetMapping("/common/download/resource") | 93 | @GetMapping("/common/download/resource") |
| 95 | - public void resourceDownload(String name, HttpServletRequest request, HttpServletResponse response) throws Exception | 94 | + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) |
| 95 | + throws Exception | ||
| 96 | { | 96 | { |
| 97 | + try | ||
| 98 | + { | ||
| 99 | + if (!FileUtils.checkAllowDownload(resource)) | ||
| 100 | + { | ||
| 101 | + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); | ||
| 102 | + } | ||
| 97 | // 本地资源路径 | 103 | // 本地资源路径 |
| 98 | String localPath = RuoYiConfig.getProfile(); | 104 | String localPath = RuoYiConfig.getProfile(); |
| 99 | // 数据库资源地址 | 105 | // 数据库资源地址 |
| 100 | - String downloadPath = localPath + StringUtils.substringAfter(name, Constants.RESOURCE_PREFIX); | 106 | + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); |
| 101 | // 下载名称 | 107 | // 下载名称 |
| 102 | String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); | 108 | String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); |
| 103 | - response.setCharacterEncoding("utf-8"); | ||
| 104 | - response.setContentType("multipart/form-data"); | ||
| 105 | - response.setHeader("Content-Disposition", | ||
| 106 | - "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, downloadName)); | 109 | + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); |
| 110 | + FileUtils.setAttachmentResponseHeader(response, downloadName); | ||
| 107 | FileUtils.writeBytes(downloadPath, response.getOutputStream()); | 111 | FileUtils.writeBytes(downloadPath, response.getOutputStream()); |
| 108 | } | 112 | } |
| 113 | + catch (Exception e) | ||
| 114 | + { | ||
| 115 | + log.error("下载文件失败", e); | ||
| 116 | + } | ||
| 117 | + } | ||
| 109 | } | 118 | } |
| 1 | +package com.ruoyi.common.utils.file; | ||
| 2 | + | ||
| 3 | +import java.io.File; | ||
| 4 | +import org.apache.commons.lang3.StringUtils; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 文件类型工具类 | ||
| 8 | + * | ||
| 9 | + * @author ruoyi | ||
| 10 | + */ | ||
| 11 | +public class FileTypeUtils | ||
| 12 | +{ | ||
| 13 | + /** | ||
| 14 | + * 获取文件类型 | ||
| 15 | + * <p> | ||
| 16 | + * 例如: ruoyi.txt, 返回: txt | ||
| 17 | + * | ||
| 18 | + * @param file 文件名 | ||
| 19 | + * @return 后缀(不含".") | ||
| 20 | + */ | ||
| 21 | + public static String getFileType(File file) | ||
| 22 | + { | ||
| 23 | + if (null == file) | ||
| 24 | + { | ||
| 25 | + return StringUtils.EMPTY; | ||
| 26 | + } | ||
| 27 | + return getFileType(file.getName()); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 获取文件类型 | ||
| 32 | + * <p> | ||
| 33 | + * 例如: ruoyi.txt, 返回: txt | ||
| 34 | + * | ||
| 35 | + * @param fileName 文件名 | ||
| 36 | + * @return 后缀(不含".") | ||
| 37 | + */ | ||
| 38 | + public static String getFileType(String fileName) | ||
| 39 | + { | ||
| 40 | + int separatorIndex = fileName.lastIndexOf("."); | ||
| 41 | + if (separatorIndex < 0) | ||
| 42 | + { | ||
| 43 | + return ""; | ||
| 44 | + } | ||
| 45 | + return fileName.substring(separatorIndex + 1).toLowerCase(); | ||
| 46 | + } | ||
| 47 | +} |
| @@ -7,7 +7,11 @@ import java.io.IOException; | @@ -7,7 +7,11 @@ import java.io.IOException; | ||
| 7 | import java.io.OutputStream; | 7 | import java.io.OutputStream; |
| 8 | import java.io.UnsupportedEncodingException; | 8 | import java.io.UnsupportedEncodingException; |
| 9 | import java.net.URLEncoder; | 9 | import java.net.URLEncoder; |
| 10 | +import java.nio.charset.StandardCharsets; | ||
| 10 | import javax.servlet.http.HttpServletRequest; | 11 | import javax.servlet.http.HttpServletRequest; |
| 12 | +import javax.servlet.http.HttpServletResponse; | ||
| 13 | +import org.apache.commons.lang3.ArrayUtils; | ||
| 14 | +import com.ruoyi.common.utils.StringUtils; | ||
| 11 | 15 | ||
| 12 | /** | 16 | /** |
| 13 | * 文件处理工具类 | 17 | * 文件处理工具类 |
| @@ -105,14 +109,37 @@ public class FileUtils extends org.apache.commons.io.FileUtils | @@ -105,14 +109,37 @@ public class FileUtils extends org.apache.commons.io.FileUtils | ||
| 105 | } | 109 | } |
| 106 | 110 | ||
| 107 | /** | 111 | /** |
| 112 | + * 检查文件是否可下载 | ||
| 113 | + * | ||
| 114 | + * @param resource 需要下载的文件 | ||
| 115 | + * @return true 正常 false 非法 | ||
| 116 | + */ | ||
| 117 | + public static boolean checkAllowDownload(String resource) | ||
| 118 | + { | ||
| 119 | + // 禁止目录上跳级别 | ||
| 120 | + if (StringUtils.contains(resource, "..")) | ||
| 121 | + { | ||
| 122 | + return false; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + // 检查允许下载的文件规则 | ||
| 126 | + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) | ||
| 127 | + { | ||
| 128 | + return true; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + // 不在允许下载的文件规则 | ||
| 132 | + return false; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + /** | ||
| 108 | * 下载文件名重新编码 | 136 | * 下载文件名重新编码 |
| 109 | * | 137 | * |
| 110 | * @param request 请求对象 | 138 | * @param request 请求对象 |
| 111 | * @param fileName 文件名 | 139 | * @param fileName 文件名 |
| 112 | * @return 编码后的文件名 | 140 | * @return 编码后的文件名 |
| 113 | */ | 141 | */ |
| 114 | - public static String setFileDownloadHeader(HttpServletRequest request, String fileName) | ||
| 115 | - throws UnsupportedEncodingException | 142 | + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException |
| 116 | { | 143 | { |
| 117 | final String agent = request.getHeader("USER-AGENT"); | 144 | final String agent = request.getHeader("USER-AGENT"); |
| 118 | String filename = fileName; | 145 | String filename = fileName; |
| @@ -139,4 +166,38 @@ public class FileUtils extends org.apache.commons.io.FileUtils | @@ -139,4 +166,38 @@ public class FileUtils extends org.apache.commons.io.FileUtils | ||
| 139 | } | 166 | } |
| 140 | return filename; | 167 | return filename; |
| 141 | } | 168 | } |
| 169 | + | ||
| 170 | + /** | ||
| 171 | + * 下载文件名重新编码 | ||
| 172 | + * | ||
| 173 | + * @param response 响应对象 | ||
| 174 | + * @param realFileName 真实文件名 | ||
| 175 | + * @return | ||
| 176 | + */ | ||
| 177 | + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException | ||
| 178 | + { | ||
| 179 | + String percentEncodedFileName = percentEncode(realFileName); | ||
| 180 | + | ||
| 181 | + StringBuilder contentDispositionValue = new StringBuilder(); | ||
| 182 | + contentDispositionValue.append("attachment; filename=") | ||
| 183 | + .append(percentEncodedFileName) | ||
| 184 | + .append(";") | ||
| 185 | + .append("filename*=") | ||
| 186 | + .append("utf-8''") | ||
| 187 | + .append(percentEncodedFileName); | ||
| 188 | + | ||
| 189 | + response.setHeader("Content-disposition", contentDispositionValue.toString()); | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + /** | ||
| 193 | + * 百分号编码工具方法 | ||
| 194 | + * | ||
| 195 | + * @param s 需要百分号编码的字符串 | ||
| 196 | + * @return 百分号编码后的字符串 | ||
| 197 | + */ | ||
| 198 | + public static String percentEncode(String s) throws UnsupportedEncodingException | ||
| 199 | + { | ||
| 200 | + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); | ||
| 201 | + return encode.replaceAll("\\+", "%20"); | ||
| 202 | + } | ||
| 142 | } | 203 | } |
-
请 注册 或 登录 后发表评论