Showing
1 changed file
with
40 additions
and
2 deletions
| ... | ... | @@ -14,6 +14,7 @@ import io.swagger.annotations.Api; |
| 14 | 14 | import io.swagger.annotations.ApiImplicitParam; |
| 15 | 15 | import io.swagger.annotations.ApiOperation; |
| 16 | 16 | import java.io.*; |
| 17 | +import java.net.URLDecoder; | |
| 17 | 18 | import java.net.URLEncoder; |
| 18 | 19 | import java.nio.charset.StandardCharsets; |
| 19 | 20 | import javax.validation.constraints.NotBlank; |
| ... | ... | @@ -60,6 +61,7 @@ public class SecurityDownloadController extends DefaultBaseController { |
| 60 | 61 | |
| 61 | 62 | String url = UploadUtil.generatePresignedUrl(record.getUploadType().getCode(), |
| 62 | 63 | record.getFilePath()); |
| 64 | + log.info("fileName===" + fileName); | |
| 63 | 65 | if (StringUtils.isNotBlank(fileName)) { |
| 64 | 66 | try { |
| 65 | 67 | url = url + "&fileName=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()) |
| ... | ... | @@ -68,6 +70,7 @@ public class SecurityDownloadController extends DefaultBaseController { |
| 68 | 70 | e.printStackTrace(); |
| 69 | 71 | } |
| 70 | 72 | } |
| 73 | + log.info("url===" + url); | |
| 71 | 74 | |
| 72 | 75 | return InvokeResultBuilder.success(url); |
| 73 | 76 | } |
| ... | ... | @@ -89,13 +92,16 @@ public class SecurityDownloadController extends DefaultBaseController { |
| 89 | 92 | throw new AccessDeniedException(); |
| 90 | 93 | } |
| 91 | 94 | |
| 92 | - String downloadFileName = StringUtils.isBlank(fileName) ? file.getName() : fileName; | |
| 95 | + log.info("fileName===" + fileName); | |
| 96 | + String normalizedFileName = normalizeFileName(fileName); | |
| 97 | + log.info("normalizedFileName===" + normalizedFileName); | |
| 98 | + String downloadFileName = StringUtils.isBlank(normalizedFileName) ? file.getName() : normalizedFileName; | |
| 93 | 99 | String encodedFileName = URLEncoder.encode(downloadFileName, StandardCharsets.UTF_8.name()) |
| 94 | 100 | .replace("+", "%20"); |
| 95 | 101 | |
| 96 | 102 | response.setContentType("application/octet-stream"); |
| 97 | 103 | response.setCharacterEncoding(StandardCharsets.UTF_8.name()); |
| 98 | - response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName); | |
| 104 | + response.setHeader("Content-Disposition", buildContentDisposition(downloadFileName, encodedFileName)); | |
| 99 | 105 | response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); |
| 100 | 106 | |
| 101 | 107 | try (InputStream in = new FileInputStream(file)) { |
| ... | ... | @@ -107,4 +113,36 @@ public class SecurityDownloadController extends DefaultBaseController { |
| 107 | 113 | response.getOutputStream().flush(); |
| 108 | 114 | } |
| 109 | 115 | } |
| 116 | + | |
| 117 | + private String buildContentDisposition(String fileName, String encodedFileName) { | |
| 118 | + String fallbackFileName = fileName | |
| 119 | + .replace("\\", "_") | |
| 120 | + .replace("\"", "_"); | |
| 121 | + return "attachment; filename=\"" + fallbackFileName + "\"; filename*=UTF-8''" | |
| 122 | + + encodedFileName; | |
| 123 | + } | |
| 124 | + | |
| 125 | + private String normalizeFileName(String fileName) { | |
| 126 | + if (StringUtils.isBlank(fileName)) { | |
| 127 | + return fileName; | |
| 128 | + } | |
| 129 | + | |
| 130 | + String normalized = fileName; | |
| 131 | + for (int i = 0; i < 2; i++) { | |
| 132 | + if (!normalized.matches(".*%[0-9a-fA-F]{2}.*")) { | |
| 133 | + break; | |
| 134 | + } | |
| 135 | + try { | |
| 136 | + String decoded = URLDecoder.decode(normalized, StandardCharsets.UTF_8.name()); | |
| 137 | + if (StringUtils.equals(decoded, normalized)) { | |
| 138 | + break; | |
| 139 | + } | |
| 140 | + normalized = decoded; | |
| 141 | + } catch (Exception e) { | |
| 142 | + break; | |
| 143 | + } | |
| 144 | + } | |
| 145 | + | |
| 146 | + return normalized; | |
| 147 | + } | |
| 110 | 148 | } | ... | ... |