-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add file size and type restriction for local file uploads (#6390)
#### What type of PR is this? /kind feature /area core #### What this PR does / why we need it: 本次 PR 为本地附件存储策略增加了对上传单文件大小和文件类型限制的功能,具体包括: 1. 单文件大小限制: 实现了对单个文件上传大小的验证功能,确保上传文件不超过设定的最大值。 2. 文件类型限制: 添加了文件类型限制功能,使用 Apache Tika 读取上传文件的 magic numbers 得到文件 mime type 并根据用户配置来决定是否允许上传 参考链接: - [List of file signatures](https://en.wikipedia.org/wiki/List_of_file_signatures) - [File Magic Numbers: The Easy way to Identify File Extensions](https://library.mosse-institute.com/articles/2022/04/file-magic-numbers-the-easy-way-to-identify-file-extensions/file-magic-numbers-the-easy-way-to-identify-file-extensions.html) #### Which issue(s) this PR fixes: Fixes #6385 #### Does this PR introduce a user-facing change? ```release-note 为本地附件存储策略增加了对上传单文件大小和文件类型限制的功能 ```
- Loading branch information
Showing
11 changed files
with
326 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
107 changes: 107 additions & 0 deletions
107
api/src/main/java/run/halo/app/infra/FileCategoryMatcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package run.halo.app.infra; | ||
|
||
import java.util.Set; | ||
|
||
/** | ||
* <p>Classifies files based on their MIME types.</p> | ||
* <p>It provides different categories such as IMAGE, SVG, AUDIO, VIDEO, ARCHIVE, and DOCUMENT. | ||
* Each category has a <code>match</code> method that checks if a given MIME type belongs to that | ||
* category.</p> | ||
* <p>The categories are defined as follows:</p> | ||
* <pre> | ||
* - IMAGE: Matches all image MIME types except for SVG. | ||
* - SVG: Specifically matches the SVG image MIME type. | ||
* - AUDIO: Matches all audio MIME types. | ||
* - VIDEO: Matches all video MIME types. | ||
* - ARCHIVE: Matches common archive MIME types like zip, rar, tar, etc. | ||
* - DOCUMENT: Matches common document MIME types like plain text, PDF, Word, Excel, etc. | ||
* </pre> | ||
* | ||
* @author guqing | ||
* @since 2.18.0 | ||
*/ | ||
public enum FileCategoryMatcher { | ||
ALL { | ||
@Override | ||
public boolean match(String mimeType) { | ||
return true; | ||
} | ||
}, | ||
IMAGE { | ||
@Override | ||
public boolean match(String mimeType) { | ||
return mimeType.startsWith("image/") && !mimeType.equals("image/svg+xml"); | ||
} | ||
}, | ||
SVG { | ||
@Override | ||
public boolean match(String mimeType) { | ||
return mimeType.equals("image/svg+xml"); | ||
} | ||
}, | ||
AUDIO { | ||
@Override | ||
public boolean match(String mimeType) { | ||
return mimeType.startsWith("audio/"); | ||
} | ||
}, | ||
VIDEO { | ||
@Override | ||
public boolean match(String mimeType) { | ||
return mimeType.startsWith("video/"); | ||
} | ||
}, | ||
ARCHIVE { | ||
static final Set<String> ARCHIVE_MIME_TYPES = Set.of( | ||
"application/zip", | ||
"application/x-rar-compressed", | ||
"application/x-tar", | ||
"application/gzip", | ||
"application/x-bzip2", | ||
"application/x-xz", | ||
"application/x-7z-compressed" | ||
); | ||
|
||
@Override | ||
public boolean match(String mimeType) { | ||
return ARCHIVE_MIME_TYPES.contains(mimeType); | ||
} | ||
}, | ||
DOCUMENT { | ||
static final Set<String> DOCUMENT_MIME_TYPES = Set.of( | ||
"text/plain", | ||
"application/rtf", | ||
"text/csv", | ||
"text/xml", | ||
"application/pdf", | ||
"application/msword", | ||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", | ||
"application/vnd.ms-excel", | ||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | ||
"application/vnd.ms-powerpoint", | ||
"application/vnd.openxmlformats-officedocument.presentationml.presentation", | ||
"application/vnd.oasis.opendocument.text", | ||
"application/vnd.oasis.opendocument.spreadsheet", | ||
"application/vnd.oasis.opendocument.presentation" | ||
); | ||
|
||
@Override | ||
public boolean match(String mimeType) { | ||
return DOCUMENT_MIME_TYPES.contains(mimeType); | ||
} | ||
}; | ||
|
||
public abstract boolean match(String mimeType); | ||
|
||
/** | ||
* Get the file category matcher by name. | ||
*/ | ||
public static FileCategoryMatcher of(String name) { | ||
for (var matcher : values()) { | ||
if (matcher.name().equalsIgnoreCase(name)) { | ||
return matcher; | ||
} | ||
} | ||
throw new IllegalArgumentException("Unsupported file category matcher for name: " + name); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
api/src/main/java/run/halo/app/infra/utils/FileTypeDetectUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package run.halo.app.infra.utils; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import lombok.experimental.UtilityClass; | ||
import org.apache.tika.Tika; | ||
import org.apache.tika.mime.MimeTypeException; | ||
import org.apache.tika.mime.MimeTypes; | ||
|
||
@UtilityClass | ||
public class FileTypeDetectUtils { | ||
|
||
private static final Tika tika = new Tika(); | ||
|
||
/** | ||
* Detect mime type. | ||
* | ||
* @param inputStream input stream will be closed after detection. | ||
*/ | ||
public static String detectMimeType(InputStream inputStream) throws IOException { | ||
try { | ||
return tika.detect(inputStream); | ||
} finally { | ||
if (inputStream != null) { | ||
inputStream.close(); | ||
} | ||
} | ||
} | ||
|
||
public static String detectFileExtension(String mimeType) throws MimeTypeException { | ||
MimeTypes mimeTypes = MimeTypes.getDefaultMimeTypes(); | ||
return mimeTypes.forName(mimeType).getExtension(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
application/src/main/java/run/halo/app/infra/exception/FileSizeExceededException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package run.halo.app.infra.exception; | ||
|
||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.server.ResponseStatusException; | ||
|
||
public class FileSizeExceededException extends ResponseStatusException { | ||
|
||
public FileSizeExceededException(String reason, String messageDetailCode, | ||
Object[] messageDetailArguments) { | ||
this(reason, null, messageDetailCode, messageDetailArguments); | ||
} | ||
|
||
public FileSizeExceededException(String reason, Throwable cause, | ||
String messageDetailCode, Object[] messageDetailArguments) { | ||
super(HttpStatus.PAYLOAD_TOO_LARGE, reason, cause, messageDetailCode, | ||
messageDetailArguments); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
application/src/main/java/run/halo/app/infra/exception/FileTypeNotAllowedException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package run.halo.app.infra.exception; | ||
|
||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.server.ResponseStatusException; | ||
|
||
public class FileTypeNotAllowedException extends ResponseStatusException { | ||
|
||
public FileTypeNotAllowedException(String reason, String messageDetailCode, | ||
Object[] messageDetailArguments) { | ||
this(reason, null, messageDetailCode, messageDetailArguments); | ||
} | ||
|
||
public FileTypeNotAllowedException(String reason, Throwable cause, | ||
String messageDetailCode, Object[] messageDetailArguments) { | ||
super(HttpStatus.UNSUPPORTED_MEDIA_TYPE, reason, cause, messageDetailCode, | ||
messageDetailArguments); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.