Full Code of zfile-dev/zfile for AI

main 3fe5f5b0b97d cached
565 files
1.1 MB
311.0k tokens
1674 symbols
1 requests
Download .txt
Showing preview only (1,307K chars total). Download the full file or copy to clipboard to get everything.
Repository: zfile-dev/zfile
Branch: main
Commit: 3fe5f5b0b97d
Files: 565
Total size: 1.1 MB

Directory structure:
gitextract_wuofd07t/

├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.yml
│       └── config.yml
├── .gitignore
├── .package/
│   └── script/
│       ├── log.sh
│       ├── restart.sh
│       ├── start.sh
│       ├── status.sh
│       ├── stop.sh
│       └── 双击我启动.bat
├── Dockerfile
├── LICENSE
├── README.md
├── pom.xml
└── src/
    └── main/
        ├── java/
        │   └── im/
        │       └── zhaojun/
        │           └── zfile/
        │               ├── ZfileApplication.java
        │               ├── core/
        │               │   ├── annotation/
        │               │   │   ├── ApiLimit.java
        │               │   │   └── DemoDisable.java
        │               │   ├── aspect/
        │               │   │   ├── ApiLimitAspect.java
        │               │   │   ├── CommonResultControllerAdvice.java
        │               │   │   └── DemoDisableAspect.java
        │               │   ├── cache/
        │               │   │   └── ZFileCacheManager.java
        │               │   ├── config/
        │               │   │   ├── ZFileProperties.java
        │               │   │   ├── datasource/
        │               │   │   │   └── DataSourceBeanPostProcessor.java
        │               │   │   ├── docs/
        │               │   │   │   └── Knife4jConfiguration.java
        │               │   │   ├── jackson/
        │               │   │   │   ├── JSONStringDeserializer.java
        │               │   │   │   └── JSONStringSerializer.java
        │               │   │   ├── mybatis/
        │               │   │   │   ├── CollectionIntegerTypeHandler.java
        │               │   │   │   ├── CollectionStrTypeHandler.java
        │               │   │   │   ├── CollectionTypeHandler.java
        │               │   │   │   ├── MyBatisPlusConfig.java
        │               │   │   │   ├── MyDatabaseIdProvider.java
        │               │   │   │   └── MyMetaObjectHandler.java
        │               │   │   ├── security/
        │               │   │   │   ├── SaSessionForJacksonCustomized.java
        │               │   │   │   ├── SaTokenConfigure.java
        │               │   │   │   ├── SaTokenDaoRedisJackson.java
        │               │   │   │   └── StpInterfaceImpl.java
        │               │   │   ├── spring/
        │               │   │   │   ├── JacksonEnumDeserializer.java
        │               │   │   │   ├── SpringCacheConfig.java
        │               │   │   │   ├── StringToEnumConverterFactory.java
        │               │   │   │   └── WebMvcConfig.java
        │               │   │   └── totp/
        │               │   │       ├── TotpAutoConfiguration.java
        │               │   │       └── TotpProperties.java
        │               │   ├── constant/
        │               │   │   ├── MdcConstant.java
        │               │   │   ├── RuleTypeConstant.java
        │               │   │   ├── ZFileConstant.java
        │               │   │   └── ZFileHttpHeaderConstant.java
        │               │   ├── controller/
        │               │   │   ├── FrontIndexController.java
        │               │   │   └── LogController.java
        │               │   ├── exception/
        │               │   │   ├── ErrorCode.java
        │               │   │   ├── GlobalExceptionHandler.java
        │               │   │   ├── biz/
        │               │   │   │   ├── APIHttpRequestBizException.java
        │               │   │   │   ├── CorsBizException.java
        │               │   │   │   ├── FilePathSecurityBizException.java
        │               │   │   │   ├── GetPreviewTextContentBizException.java
        │               │   │   │   ├── InitializeStorageSourceBizException.java
        │               │   │   │   ├── InvalidStorageSourceBizException.java
        │               │   │   │   ├── StorageSourceFileForbiddenAccessBizException.java
        │               │   │   │   └── StorageSourceIllegalOperationBizException.java
        │               │   │   ├── core/
        │               │   │   │   ├── BizException.java
        │               │   │   │   ├── ErrorPageBizException.java
        │               │   │   │   └── SystemException.java
        │               │   │   ├── status/
        │               │   │   │   ├── BadRequestAccessException.java
        │               │   │   │   ├── ForbiddenAccessException.java
        │               │   │   │   ├── MethodNotAllowedAccessException.java
        │               │   │   │   ├── NotFoundAccessException.java
        │               │   │   │   └── UnauthorizedAccessException.java
        │               │   │   └── system/
        │               │   │       ├── UploadFileFailSystemException.java
        │               │   │       └── ZFileAuthorizationSystemException.java
        │               │   ├── filter/
        │               │   │   ├── CorsFilter.java
        │               │   │   ├── MDCFilter.java
        │               │   │   └── SecurityFilter.java
        │               │   ├── io/
        │               │   │   └── EnsureContentLengthInputStreamResource.java
        │               │   ├── model/
        │               │   │   └── request/
        │               │   │       └── PageQueryRequest.java
        │               │   ├── util/
        │               │   │   ├── AjaxJson.java
        │               │   │   ├── ArrayUtils.java
        │               │   │   ├── CharPool.java
        │               │   │   ├── CharSequenceUtil.java
        │               │   │   ├── ClassUtils.java
        │               │   │   ├── CollectionUtils.java
        │               │   │   ├── DnsUtil.java
        │               │   │   ├── EnumConvertUtils.java
        │               │   │   ├── FileComparator.java
        │               │   │   ├── FileResponseUtil.java
        │               │   │   ├── FileSizeConverter.java
        │               │   │   ├── FileUtils.java
        │               │   │   ├── HttpUtil.java
        │               │   │   ├── NaturalOrderComparator.java
        │               │   │   ├── NumberUtils.java
        │               │   │   ├── OnlyOfficeKeyCacheUtils.java
        │               │   │   ├── PatternMatcherUtils.java
        │               │   │   ├── PlaceholderUtils.java
        │               │   │   ├── ProxyDownloadUrlUtils.java
        │               │   │   ├── RequestHolder.java
        │               │   │   ├── RequestUtils.java
        │               │   │   ├── SizeToStrUtils.java
        │               │   │   ├── SpringMvcUtils.java
        │               │   │   ├── StrPool.java
        │               │   │   ├── StringUtils.java
        │               │   │   ├── UrlUtils.java
        │               │   │   ├── ZFileAuthUtil.java
        │               │   │   └── matcher/
        │               │   │       ├── AbstractRuleMatcher.java
        │               │   │       ├── IRuleMatcher.java
        │               │   │       ├── RuleMatcherFactory.java
        │               │   │       └── impl/
        │               │   │           ├── AntPathRuleMatcher.java
        │               │   │           ├── IpRuleMatcher.java
        │               │   │           ├── RegexRuleMatcher.java
        │               │   │           └── SpringSimpleRuleMatcher.java
        │               │   └── validation/
        │               │       ├── StringListValue.java
        │               │       └── StringListValueConstraintValidator.java
        │               └── module/
        │                   ├── admin/
        │                   │   ├── controller/
        │                   │   │   ├── IpHelperController.java
        │                   │   │   └── RuleMatcherTestController.java
        │                   │   └── model/
        │                   │       └── request/
        │                   │           └── TestRuleMatcherRequest.java
        │                   ├── config/
        │                   │   ├── annotation/
        │                   │   │   └── JSONStringParse.java
        │                   │   ├── constant/
        │                   │   │   └── SystemConfigConstant.java
        │                   │   ├── controller/
        │                   │   │   ├── SettingController.java
        │                   │   │   └── SiteController.java
        │                   │   ├── event/
        │                   │   │   ├── DirectLinkPrefixModifyHandler.java
        │                   │   │   ├── ISystemConfigModifyHandler.java
        │                   │   │   ├── SecureLoginEntryModifyHandler.java
        │                   │   │   └── SystemConfigModifyHandlerChain.java
        │                   │   ├── mapper/
        │                   │   │   └── SystemConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── dto/
        │                   │   │   │   ├── LinkExpireDTO.java
        │                   │   │   │   └── SystemConfigDTO.java
        │                   │   │   ├── entity/
        │                   │   │   │   └── SystemConfig.java
        │                   │   │   ├── enums/
        │                   │   │   │   └── FileClickModeEnum.java
        │                   │   │   ├── request/
        │                   │   │   │   ├── UpdateAccessSettingRequest.java
        │                   │   │   │   ├── UpdateLinkSettingRequest.java
        │                   │   │   │   ├── UpdateSecuritySettingRequest.java
        │                   │   │   │   ├── UpdateSiteSettingRequest.java
        │                   │   │   │   ├── UpdateUserNameAndPasswordRequest.java
        │                   │   │   │   └── UpdateViewSettingRequest.java
        │                   │   │   └── result/
        │                   │   │       └── FrontSiteConfigResult.java
        │                   │   └── service/
        │                   │       └── SystemConfigService.java
        │                   ├── filter/
        │                   │   ├── controller/
        │                   │   │   └── StorageSourceFilterController.java
        │                   │   ├── mapper/
        │                   │   │   └── FilterConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   └── FilterConfig.java
        │                   │   │   └── enums/
        │                   │   │       └── FilterConfigHiddenModeEnum.java
        │                   │   └── service/
        │                   │       └── FilterConfigService.java
        │                   ├── install/
        │                   │   ├── controller/
        │                   │   │   └── InstallController.java
        │                   │   ├── model/
        │                   │   │   └── request/
        │                   │   │       └── InstallSystemRequest.java
        │                   │   └── service/
        │                   │       └── InstallService.java
        │                   ├── link/
        │                   │   ├── aspect/
        │                   │   │   ├── LinkRateLimiterAspect.java
        │                   │   │   └── RefererCheckAspect.java
        │                   │   ├── cache/
        │                   │   │   └── LinkRateLimiterCache.java
        │                   │   ├── controller/
        │                   │   │   ├── DirectLinkController.java
        │                   │   │   ├── ShortLinkController.java
        │                   │   │   └── ShortLinkManagerController.java
        │                   │   ├── convert/
        │                   │   │   └── ShortLinkConvert.java
        │                   │   ├── dto/
        │                   │   │   └── DynamicRegisterMappingHandlerDTO.java
        │                   │   ├── event/
        │                   │   │   └── DeleteExpireLinkEvent.java
        │                   │   ├── mapper/
        │                   │   │   └── ShortLinkMapper.java
        │                   │   ├── model/
        │                   │   │   ├── dto/
        │                   │   │   │   └── CacheInfo.java
        │                   │   │   ├── entity/
        │                   │   │   │   └── ShortLink.java
        │                   │   │   ├── enums/
        │                   │   │   │   └── RefererTypeEnum.java
        │                   │   │   ├── request/
        │                   │   │   │   ├── BatchDeleteRequest.java
        │                   │   │   │   ├── BatchGenerateLinkRequest.java
        │                   │   │   │   ├── QueryDownloadLogRequest.java
        │                   │   │   │   ├── QueryLoginLogRequest.java
        │                   │   │   │   ├── QueryShortLinkLogRequest.java
        │                   │   │   │   ├── ShortLinkResult.java
        │                   │   │   │   └── ShortLinkSearchRequest.java
        │                   │   │   └── result/
        │                   │   │       └── BatchGenerateLinkResponse.java
        │                   │   └── service/
        │                   │       ├── DynamicDirectLinkPrefixService.java
        │                   │       ├── LinkDownloadService.java
        │                   │       └── ShortLinkService.java
        │                   ├── log/
        │                   │   ├── controller/
        │                   │   │   ├── DownloadLogManagerController.java
        │                   │   │   └── LoginLogController.java
        │                   │   ├── convert/
        │                   │   │   └── DownloadLogConvert.java
        │                   │   ├── mapper/
        │                   │   │   ├── DownloadLogMapper.java
        │                   │   │   └── LoginLogMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   ├── DownloadLog.java
        │                   │   │   │   └── LoginLog.java
        │                   │   │   └── result/
        │                   │   │       └── DownloadLogResult.java
        │                   │   └── service/
        │                   │       ├── DownloadLogService.java
        │                   │       └── LoginLogService.java
        │                   ├── onlyoffice/
        │                   │   ├── controller/
        │                   │   │   └── OnlyOfficeController.java
        │                   │   └── model/
        │                   │       ├── OnlyOfficeCallback.java
        │                   │       └── OnlyOfficeFile.java
        │                   ├── password/
        │                   │   ├── controller/
        │                   │   │   └── StorageSourcePasswordController.java
        │                   │   ├── mapper/
        │                   │   │   └── PasswordConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── dto/
        │                   │   │   │   └── VerifyResultDTO.java
        │                   │   │   └── entity/
        │                   │   │       └── PasswordConfig.java
        │                   │   └── service/
        │                   │       └── PasswordConfigService.java
        │                   ├── permission/
        │                   │   ├── controller/
        │                   │   │   ├── PermissionController.java
        │                   │   │   └── StorageSourcePermissionController.java
        │                   │   ├── convert/
        │                   │   │   └── PermissionConfigConvert.java
        │                   │   ├── mapper/
        │                   │   │   └── PermissionConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   └── PermissionConfig.java
        │                   │   │   └── result/
        │                   │   │       ├── PermissionConfigResult.java
        │                   │   │       └── PermissionInfoResult.java
        │                   │   └── service/
        │                   │       └── PermissionConfigService.java
        │                   ├── readme/
        │                   │   ├── controller/
        │                   │   │   └── StorageSourceReadmeController.java
        │                   │   ├── mapper/
        │                   │   │   └── ReadmeConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   └── ReadmeConfig.java
        │                   │   │   └── enums/
        │                   │   │       ├── ReadmeDisplayModeEnum.java
        │                   │   │       └── ReadmePathModeEnum.java
        │                   │   └── service/
        │                   │       └── ReadmeConfigService.java
        │                   ├── share/
        │                   │   ├── context/
        │                   │   │   └── ShareAccessContext.java
        │                   │   ├── controller/
        │                   │   │   ├── ShareFileManagerController.java
        │                   │   │   └── ShareLinkController.java
        │                   │   ├── mapper/
        │                   │   │   └── ShareLinkMapper.java
        │                   │   ├── model/
        │                   │   │   ├── dto/
        │                   │   │   │   └── ShareEntryDTO.java
        │                   │   │   ├── entity/
        │                   │   │   │   └── ShareLink.java
        │                   │   │   ├── enums/
        │                   │   │   │   ├── ShareEntryTypeEnum.java
        │                   │   │   │   └── ShareTypeEnum.java
        │                   │   │   ├── request/
        │                   │   │   │   ├── CreateShareLinkRequest.java
        │                   │   │   │   ├── ShareFileListRequest.java
        │                   │   │   │   ├── ShareLinkListRequest.java
        │                   │   │   │   └── VerifySharePasswordRequest.java
        │                   │   │   └── result/
        │                   │   │       ├── CreateShareLinkResult.java
        │                   │   │       ├── ShareFileInfoResult.java
        │                   │   │       └── ShareLinkResult.java
        │                   │   └── service/
        │                   │       ├── ShareLinkFileService.java
        │                   │       └── ShareLinkService.java
        │                   ├── sso/
        │                   │   ├── controller/
        │                   │   │   ├── SsoAPIController.java
        │                   │   │   ├── SsoController.java
        │                   │   │   └── SsoManagerController.java
        │                   │   ├── mapper/
        │                   │   │   └── SsoConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   └── SsoConfig.java
        │                   │   │   ├── request/
        │                   │   │   │   └── CheckProviderDuplicateRequest.java
        │                   │   │   └── response/
        │                   │   │       ├── SsoLoginItemResponse.java
        │                   │   │       └── TokenResponse.java
        │                   │   └── service/
        │                   │       └── SsoService.java
        │                   ├── storage/
        │                   │   ├── annotation/
        │                   │   │   ├── CheckPassword.java
        │                   │   │   ├── CheckPasswords.java
        │                   │   │   ├── LinkRateLimiter.java
        │                   │   │   ├── ProCheck.java
        │                   │   │   ├── RefererCheck.java
        │                   │   │   ├── StorageParamItem.java
        │                   │   │   ├── StorageParamSelect.java
        │                   │   │   ├── StorageParamSelectOption.java
        │                   │   │   ├── StoragePermissionCheck.java
        │                   │   │   └── impl/
        │                   │   │       └── EncodingStorageParamSelect.java
        │                   │   ├── aspect/
        │                   │   │   ├── CheckPasswordAspect.java
        │                   │   │   └── FileOperatorCheckAspect.java
        │                   │   ├── chain/
        │                   │   │   ├── FileChain.java
        │                   │   │   ├── FileContext.java
        │                   │   │   └── command/
        │                   │   │       ├── FileAccessPermissionVerifyCommand.java
        │                   │   │       ├── FileDownloadPermissionCommand.java
        │                   │   │       ├── FileHiddenCommand.java
        │                   │   │       ├── FileSortCommand.java
        │                   │   │       └── FolderPasswordVerifyCommand.java
        │                   │   ├── constant/
        │                   │   │   ├── S3SignerTypeConstant.java
        │                   │   │   ├── StorageConfigConstant.java
        │                   │   │   └── StorageSourceConnectionProperties.java
        │                   │   ├── context/
        │                   │   │   ├── StorageSourceContext.java
        │                   │   │   └── StorageSourceInitializer.java
        │                   │   ├── controller/
        │                   │   │   ├── base/
        │                   │   │   │   ├── StorageMetaDataController.java
        │                   │   │   │   └── StorageSourceController.java
        │                   │   │   ├── callback/
        │                   │   │   │   ├── GoogleDriveCallbackController.java
        │                   │   │   │   └── OneDriveCallbackController.java
        │                   │   │   ├── file/
        │                   │   │   │   ├── FileController.java
        │                   │   │   │   └── FileOperatorController.java
        │                   │   │   ├── helper/
        │                   │   │   │   ├── GoogleDriveHelperController.java
        │                   │   │   │   ├── Open115HelperController.java
        │                   │   │   │   ├── Open115UploadUtils.java
        │                   │   │   │   ├── S3HelperController.java
        │                   │   │   │   └── SharePointHelperController.java
        │                   │   │   └── proxy/
        │                   │   │       ├── Open115UrlController.java
        │                   │   │       ├── ProxyDownloadController.java
        │                   │   │       └── ProxyUploadController.java
        │                   │   ├── convert/
        │                   │   │   └── StorageSourceConvert.java
        │                   │   ├── enums/
        │                   │   │   └── StorageParamItemAnnoEnum.java
        │                   │   ├── event/
        │                   │   │   ├── StorageSourceCopyEvent.java
        │                   │   │   └── StorageSourceDeleteEvent.java
        │                   │   ├── function/
        │                   │   │   ├── AllowAdminFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── AllowAllFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── BasicFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── DisableAllFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── LinkFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── SearchFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   └── ShortLinkFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   ├── mapper/
        │                   │   │   ├── StorageSourceConfigMapper.java
        │                   │   │   └── StorageSourceMapper.java
        │                   │   ├── model/
        │                   │   │   ├── bo/
        │                   │   │   │   ├── AuthModel.java
        │                   │   │   │   ├── RefreshTokenCacheBO.java
        │                   │   │   │   ├── StorageSourceMetadata.java
        │                   │   │   │   ├── StorageSourceParamDef.java
        │                   │   │   │   └── UploadSignParam.java
        │                   │   │   ├── dto/
        │                   │   │   │   ├── FileOperatorTypeDefaultValueDTO.java
        │                   │   │   │   ├── OAuth2TokenDTO.java
        │                   │   │   │   ├── RefreshTokenInfoDTO.java
        │                   │   │   │   ├── StorageSourceAllParamDTO.java
        │                   │   │   │   ├── StorageSourceDTO.java
        │                   │   │   │   ├── StorageSourceInitDTO.java
        │                   │   │   │   └── ZFileCORSRule.java
        │                   │   │   ├── entity/
        │                   │   │   │   ├── StorageSource.java
        │                   │   │   │   └── StorageSourceConfig.java
        │                   │   │   ├── enums/
        │                   │   │   │   ├── FileOperatorTypeEnum.java
        │                   │   │   │   ├── FileTypeEnum.java
        │                   │   │   │   ├── SearchFolderModeEnum.java
        │                   │   │   │   ├── SearchModeEnum.java
        │                   │   │   │   ├── StorageParamTypeEnum.java
        │                   │   │   │   └── StorageTypeEnum.java
        │                   │   │   ├── param/
        │                   │   │   │   ├── AliyunParam.java
        │                   │   │   │   ├── DogeCloudParam.java
        │                   │   │   │   ├── FtpParam.java
        │                   │   │   │   ├── GoogleDriveParam.java
        │                   │   │   │   ├── HuaweiParam.java
        │                   │   │   │   ├── IStorageParam.java
        │                   │   │   │   ├── LocalParam.java
        │                   │   │   │   ├── MicrosoftDriveParam.java
        │                   │   │   │   ├── MinIOParam.java
        │                   │   │   │   ├── OneDriveChinaParam.java
        │                   │   │   │   ├── OneDriveParam.java
        │                   │   │   │   ├── Open115Param.java
        │                   │   │   │   ├── OptionalProxyTransferParam.java
        │                   │   │   │   ├── ProxyTransferParam.java
        │                   │   │   │   ├── QiniuParam.java
        │                   │   │   │   ├── S3BaseParam.java
        │                   │   │   │   ├── S3Param.java
        │                   │   │   │   ├── SftpParam.java
        │                   │   │   │   ├── SharePointChinaParam.java
        │                   │   │   │   ├── SharePointParam.java
        │                   │   │   │   ├── TencentParam.java
        │                   │   │   │   ├── UpYunParam.java
        │                   │   │   │   └── WebdavParam.java
        │                   │   │   ├── request/
        │                   │   │   │   ├── GetGoogleDriveListRequest.java
        │                   │   │   │   ├── GetS3BucketListRequest.java
        │                   │   │   │   ├── GetS3CorsListRequest.java
        │                   │   │   │   ├── SharePointInfoRequest.java
        │                   │   │   │   ├── SharePointSearchSitesRequest.java
        │                   │   │   │   ├── SharePointSiteListsRequest.java
        │                   │   │   │   ├── admin/
        │                   │   │   │   │   ├── CopyStorageSourceRequest.java
        │                   │   │   │   │   ├── UpdateStorageIdRequest.java
        │                   │   │   │   │   └── UpdateStorageSortRequest.java
        │                   │   │   │   ├── base/
        │                   │   │   │   │   ├── FileItemRequest.java
        │                   │   │   │   │   ├── FileListConfigRequest.java
        │                   │   │   │   │   ├── FileListRequest.java
        │                   │   │   │   │   ├── SaveStorageSourceRequest.java
        │                   │   │   │   │   └── SearchStorageRequest.java
        │                   │   │   │   └── operator/
        │                   │   │   │       ├── BatchDeleteRequest.java
        │                   │   │   │       ├── BatchMoveOrCopyFileRequest.java
        │                   │   │   │       ├── NewFolderRequest.java
        │                   │   │   │       ├── RenameFileRequest.java
        │                   │   │   │       ├── RenameFolderRequest.java
        │                   │   │   │       └── UploadFileRequest.java
        │                   │   │   └── result/
        │                   │   │       ├── FileInfoResult.java
        │                   │   │       ├── FileItemResult.java
        │                   │   │       ├── GoogleDriveInfoResult.java
        │                   │   │       ├── Open115AuthDeviceCodeResult.java
        │                   │   │       ├── Open115GetStatusResult.java
        │                   │   │       ├── S3BucketNameResult.java
        │                   │   │       ├── SharepointSiteListResult.java
        │                   │   │       ├── SharepointSiteResult.java
        │                   │   │       ├── StorageSourceAdminResult.java
        │                   │   │       ├── StorageSourceConfigResult.java
        │                   │   │       ├── StorageSourceResult.java
        │                   │   │       └── operator/
        │                   │   │           └── BatchOperatorResult.java
        │                   │   ├── oauth2/
        │                   │   │   └── service/
        │                   │   │       ├── AbstractMicrosoftOAuth2Service.java
        │                   │   │       ├── GoogleDriveOAuth2ServiceImpl.java
        │                   │   │       ├── IOAuth2Service.java
        │                   │   │       ├── OneDriveChinaOAuth2ServiceImpl.java
        │                   │   │       └── OneDriveOAuth2ServiceImpl.java
        │                   │   ├── service/
        │                   │   │   ├── StorageSourceConfigService.java
        │                   │   │   ├── StorageSourceService.java
        │                   │   │   ├── base/
        │                   │   │   │   ├── AbstractBaseFileService.java
        │                   │   │   │   ├── AbstractMicrosoftDriveService.java
        │                   │   │   │   ├── AbstractOneDriveServiceBase.java
        │                   │   │   │   ├── AbstractProxyTransferService.java
        │                   │   │   │   ├── AbstractS3BaseFileService.java
        │                   │   │   │   ├── AbstractSharePointServiceBase.java
        │                   │   │   │   ├── BaseFileService.java
        │                   │   │   │   └── RefreshTokenService.java
        │                   │   │   └── impl/
        │                   │   │       ├── AliyunServiceImpl.java
        │                   │   │       ├── DogeCloudServiceImpl.java
        │                   │   │       ├── FtpServiceImpl.java
        │                   │   │       ├── GoogleDriveServiceImpl.java
        │                   │   │       ├── HuaweiServiceImpl.java
        │                   │   │       ├── LocalServiceImpl.java
        │                   │   │       ├── MinIOServiceImpl.java
        │                   │   │       ├── OneDriveChinaServiceImpl.java
        │                   │   │       ├── OneDriveServiceImpl.java
        │                   │   │       ├── Open115ServiceImpl.java
        │                   │   │       ├── QiniuServiceImpl.java
        │                   │   │       ├── S3ServiceImpl.java
        │                   │   │       ├── SftpServiceImpl.java
        │                   │   │       ├── SharePointChinaServiceImpl.java
        │                   │   │       ├── SharePointServiceImpl.java
        │                   │   │       ├── TencentServiceImpl.java
        │                   │   │       ├── UpYunServiceImpl.java
        │                   │   │       └── WebdavServiceImpl.java
        │                   │   └── support/
        │                   │       ├── Open115IdCacheService.java
        │                   │       ├── StorageSourceSupport.java
        │                   │       ├── ftp/
        │                   │       │   ├── FtpClientFactory.java
        │                   │       │   └── FtpClientPool.java
        │                   │       ├── sftp/
        │                   │       │   ├── SFtpClientFactory.java
        │                   │       │   └── SFtpClientPool.java
        │                   │       └── webdav/
        │                   │           └── CustomSardine.java
        │                   └── user/
        │                       ├── aspect/
        │                       │   └── LoginLogAspect.java
        │                       ├── controller/
        │                       │   ├── AdminTwoFAController.java
        │                       │   ├── UserController.java
        │                       │   └── UserManagerController.java
        │                       ├── event/
        │                       │   ├── UserCopyEvent.java
        │                       │   └── UserDeleteEvent.java
        │                       ├── manager/
        │                       │   └── UserManager.java
        │                       ├── mapper/
        │                       │   ├── UserMapper.java
        │                       │   └── UserStorageSourceMapper.java
        │                       ├── model/
        │                       │   ├── constant/
        │                       │   │   └── UserConstant.java
        │                       │   ├── dto/
        │                       │   │   └── UserStorageSourceDetailDTO.java
        │                       │   ├── entity/
        │                       │   │   ├── User.java
        │                       │   │   └── UserStorageSource.java
        │                       │   ├── enums/
        │                       │   │   ├── LoginLogModeEnum.java
        │                       │   │   └── LoginVerifyModeEnum.java
        │                       │   ├── request/
        │                       │   │   ├── CheckUserDuplicateRequest.java
        │                       │   │   ├── CopyUserRequest.java
        │                       │   │   ├── QueryUserRequest.java
        │                       │   │   ├── ResetAdminUserNameAndPasswordRequest.java
        │                       │   │   ├── SaveUserRequest.java
        │                       │   │   ├── UpdateUserPwdRequest.java
        │                       │   │   ├── UserLoginRequest.java
        │                       │   │   └── VerifyLoginTwoFactorAuthenticatorRequest.java
        │                       │   ├── response/
        │                       │   │   └── UserDetailResponse.java
        │                       │   └── result/
        │                       │       ├── CheckLoginResult.java
        │                       │       ├── LoginResult.java
        │                       │       ├── LoginTwoFactorAuthenticatorResult.java
        │                       │       └── LoginVerifyImgResult.java
        │                       ├── service/
        │                       │   ├── DynamicLoginEntryService.java
        │                       │   ├── UserService.java
        │                       │   ├── UserStorageSourceService.java
        │                       │   └── login/
        │                       │       ├── ImgVerifyCodeService.java
        │                       │       ├── LoginService.java
        │                       │       ├── TwoFactorAuthenticatorVerifyService.java
        │                       │       └── verify/
        │                       │           ├── LoginVerifyService.java
        │                       │           └── impl/
        │                       │               ├── ImgCodeLoginVerifyService.java
        │                       │               ├── PasswordVerifyService.java
        │                       │               └── TwoFactorAuthLoginVerifyService.java
        │                       ├── util/
        │                       │   └── LoginEntryPathUtils.java
        │                       └── utils/
        │                           └── PasswordVerifyUtils.java
        └── resources/
            ├── META-INF/
            │   └── additional-spring-configuration-metadata.json
            ├── application-default.properties
            ├── application-dev.properties
            ├── application-prod.properties
            ├── application.properties
            ├── banner.txt
            ├── db/
            │   ├── migration-mysql/
            │   │   ├── R__data.sql
            │   │   ├── V10__system_config_add_field_webdav.sql
            │   │   ├── V11__system_config_modify_field_only_office_url_to_https.sql
            │   │   ├── V12__system_config_modify_field_value_to_text.sql
            │   │   ├── V13__system_config_add_field_allow_path_link_anon_access.sql
            │   │   ├── V14__system_config_add_field_load_more_size.sql
            │   │   ├── V15__system_config_add_field_site_home_name.sql
            │   │   ├── V16__system_config_add_field_default_sort_field.sql
            │   │   ├── V17__system_config_add_field_link_limit_field.sql
            │   │   ├── V18__download_log_add_field_download_type.sql
            │   │   ├── V19__short_link_add_field_expire_date.sql
            │   │   ├── V1__Base_version.sql
            │   │   ├── V20__system_config_add_field_favicon_url_field.sql
            │   │   ├── V21__system_config_add_field_expire_times_field.sql
            │   │   ├── V22__system_config_add_field_default_save_pwd_field.sql
            │   │   ├── V23__system_config_add_field_only_office_secret_field.sql
            │   │   ├── V24__system_config_add_field_enable_hover_menu_field.sql
            │   │   ├── V25__system_config_add_field_site_access_field.sql
            │   │   ├── V26__system_config_add_field_login_verify.sql
            │   │   ├── V27__add_table_login_log.sql
            │   │   ├── V28__add_multi_user.sql
            │   │   ├── V29__system_config_add_field_login_verify.sql
            │   │   ├── V2__download_log_modify_storage_key_field_length.sql
            │   │   ├── V30__delete_storage_source_auto_cors_config.sql
            │   │   ├── V31__system_config_add_field_webdav.sql
            │   │   ├── V32__system_config_delete_domain_field.sql
            │   │   ├── V33__storage_source_config_update_field.sql
            │   │   ├── V34__storage_source_config_update_field.sql
            │   │   ├── V35__system_config_add_field_login_log_mode.sql
            │   │   ├── V36__user_add_field_salt.sql
            │   │   ├── V37__set_login_log_model_default_off.sql
            │   │   ├── V38__update_login_log_ip_field_length.sql
            │   │   ├── V3__system_config_add_field_file_click_mode.sql
            │   │   ├── V40__system_config_add_field_mobile_layout.sql
            │   │   ├── V41__system_config_add_custom_office_suffix.sql
            │   │   ├── V42__system_config_add_guest_index_html.sql
            │   │   ├── V43__set_2fa_default_value.sql
            │   │   ├── V44__system_config_add_mobile_.sql
            │   │   ├── V45__add_sso_config.sql
            │   │   ├── V46__add_template_user.sql
            │   │   ├── V47__system_config_add_force_backend_address.sql
            │   │   ├── V48__system_config_add_field_kkfileview_url.sql
            │   │   ├── V49__system_config_add_custom_kkfileview_suffix.sql
            │   │   ├── V4__download_log_modify_ip_field_length.sql
            │   │   ├── V50__system_config_add_kkfileview_open_mode.sql
            │   │   ├── V51__storage_source_config_add_refresh_token_expired_at.sql
            │   │   ├── V52__ststem_config_add_mobile_show_file_size.sql
            │   │   ├── V53__readme_config_add_path_mode_field.sql
            │   │   ├── V54__add_share_link_table.sql
            │   │   ├── V55__system_config_add_secure_login_entry.sql
            │   │   ├── V56__system_config_add_download_confirm_flags.sql
            │   │   ├── V57__user_add_default_share_permissions.sql
            │   │   ├── V5__add_permission_config_table.sql
            │   │   ├── V6__system_config_add_field_auth_code.sql
            │   │   ├── V7__system_config_add_field_max_file_uploads.sql
            │   │   ├── V8__storage_source_add_field_compatibility_readme.sql
            │   │   └── V9__system_config_add_field_only_office_url.sql
            │   └── migration-sqlite/
            │       ├── R__data.sql
            │       ├── V10__system_config_add_field_webdav.sql
            │       ├── V11__system_config_modify_field_only_office_url_to_https.sql
            │       ├── V12__system_config_modify_field_value_to_text.sql
            │       ├── V13__system_config_add_field_allow_path_link_anon_access.sql
            │       ├── V14__system_config_add_field_load_more_size.sql
            │       ├── V15__system_config_add_field_site_home_name.sql
            │       ├── V16__system_config_add_field_default_sort_field.sql
            │       ├── V17__system_config_add_field_link_limit_field.sql
            │       ├── V18__download_log_add_field_download_type.sql
            │       ├── V19__short_link_add_field_expire_date.sql
            │       ├── V1__Base_version.sql
            │       ├── V20__system_config_add_field_favicon_url_field.sql
            │       ├── V21__system_config_add_field_expire_times_field.sql
            │       ├── V22__system_config_add_field_default_save_pwd_field.sql
            │       ├── V23__system_config_add_field_only_office_secret_field.sql
            │       ├── V24__system_config_add_field_enable_hover_menu_field.sql
            │       ├── V25__system_config_add_field_site_access_field.sql
            │       ├── V26__system_config_add_field_login_verify.sql
            │       ├── V27__add_table_login_log.sql
            │       ├── V28__add_multi_user.sql
            │       ├── V29__system_config_add_field_login_verify.sql
            │       ├── V2__download_log_modify_storage_key_field_length.sql
            │       ├── V30__delete_storage_source_auto_cors_config.sql
            │       ├── V31__system_config_add_field_webdav.sql
            │       ├── V32__system_config_delete_domain_field.sql
            │       ├── V33__storage_source_config_update_field.sql
            │       ├── V34__storage_source_config_update_field.sql
            │       ├── V35__system_config_add_field_login_log_mode.sql
            │       ├── V36__user_add_field_salt.sql
            │       ├── V37__fix_user_create_time_field_to_timestamp.sql
            │       ├── V38__set_login_log_model_default_off.sql
            │       ├── V3__system_config_add_field_file_click_mode.sql
            │       ├── V40__system_config_add_field_mobile_layout.sql
            │       ├── V41__system_config_add_custom_office_suffix.sql
            │       ├── V42__system_config_add_guest_index_html.sql
            │       ├── V43__set_2fa_default_value.sql
            │       ├── V44__system_config_add_mobile_.sql
            │       ├── V45__add_sso_config.sql
            │       ├── V46__add_template_user.sql
            │       ├── V47__system_config_add_force_backend_address.sql
            │       ├── V48__system_config_add_field_kkfileview_url.sql
            │       ├── V49__system_config_add_custom_kkfileview_suffix.sql
            │       ├── V4__download_log_modify_ip_field_length.sql
            │       ├── V50__system_config_add_kkfileview_open_mode.sql
            │       ├── V51__storage_source_config_add_refresh_token_expired_at.sql
            │       ├── V52__ststem_config_add_mobile_show_file_size.sql
            │       ├── V53__readme_config_add_path_mode_field.sql
            │       ├── V54__add_share_link_table.sql
            │       ├── V55__system_config_add_secure_login_entry.sql
            │       ├── V56__system_config_add_download_confirm_flags.sql
            │       ├── V57__user_add_default_share_permissions.sql
            │       ├── V5__add_permission_config_table.sql
            │       ├── V6__system_config_add_field_auth_code.sql
            │       ├── V7__system_config_add_field_max_file_uploads.sql
            │       ├── V8__storage_source_add_field_compatibility_readme.sql
            │       └── V9__system_config_add_field_only_office_url.sql
            ├── logback-spring.xml
            ├── mapper/
            │   ├── DownloadLogMapper.xml
            │   ├── FilterConfigMapper.xml
            │   ├── LoginLogMapper.xml
            │   ├── PasswordConfigMapper.xml
            │   ├── PermissionConfigMapper.xml
            │   ├── ReadmeConfigMapper.xml
            │   ├── ShareLinkMapper.xml
            │   ├── ShortLinkMapper.xml
            │   ├── SsoConfigMapper.xml
            │   ├── StorageConfigMapper.xml
            │   ├── StorageSourceMapper.xml
            │   ├── SystemConfigMapper.xml
            │   ├── UserMapper.xml
            │   └── UserStorageSourceMapper.xml
            └── templates/
                ├── callback.html
                └── error/
                    └── 404.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: 'Blank Issue'
description: 请使用 https://issue.zfile.vip 创建新的问题.
body:
  - type: markdown
    attributes:
      value: |
        **注意:**
        不要通过此页面创建问题, 请使用 https://issue.zfile.vip 创建新的问题.
        如果不是通过此链接创建的问题, 将会被直接关闭.
  - type: textarea
    id: add-a-description
    attributes:
      label: Add a description

================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
  - name: 创建 Issue
    url: https://issue.zfile.vip/
    about: 未通过 https://issue.zfile.vip/ 创建的问题可能会被立即关闭。

================================================
FILE: .gitignore
================================================
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
.fastRequest
.murphy.yml

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
/.mvn/wrapper/
/mvnw
/mvnw.cmd
/result/

================================================
FILE: .package/script/log.sh
================================================
#!/bin/bash
tail -fn100 ~/.zfile-v4/logs/zfile.log

================================================
FILE: .package/script/restart.sh
================================================
#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

$DIR/stop.sh
$DIR/start.sh

================================================
FILE: .package/script/start.sh
================================================
#!/bin/bash

# 检测是否已启动
pid=`ps -ef | grep -n zfile | grep -v grep | grep -v launch | grep -v .sh | awk '{print $2}'`
if [ -n "${pid}" ]
then
   echo "已运行在 pid:${pid},无需重复启动!"
   exit 0
fi

# 获取当前脚本所在路径
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ZFILE_DIR=$(dirname "$DIR")

# 启动 zfile
nohup $ZFILE_DIR/zfile/zfile --spring.config.location=$ZFILE_DIR/application.properties --spring.web.resources.static-locations=file:$ZFILE_DIR/static/  >/dev/null 2>&1 &
echo '启动中...'
sleep 3s

# 输出 pid
pid=`ps -ef | grep -n zfile | grep -v grep | grep -v .sh | awk '{print $2}'`
echo "目前 PID 为: ${pid}"


================================================
FILE: .package/script/status.sh
================================================
#!/bin/bash

echo "------------------ 检测状态 START --------------"
pid=`ps -ef | grep -n zfile | grep -v grep | grep -v launch | grep -v .sh | awk '{print $2}'`
if [ -z "${pid}" ]
then
   echo "未运行, 无需停止!"
else
   echo "运行pid:${pid}"
fi

echo "------------------ 检测状态  END  --------------"

================================================
FILE: .package/script/stop.sh
================================================
#!/bin/bash

echo "------------------ 检测状态 START --------------"
pid=`ps -ef | grep -n zfile | grep -v grep | grep -v .sh | awk '{print $2}'`
if [ -z "${pid}" ]
then
   echo "未运行, 无需停止!"
else
   echo "运行pid:${pid}"
   kill -9 ${pid}
   echo "已停止进程: ${pid}"
fi

echo "------------------ 检测状态  END  --------------"

================================================
FILE: .package/script/双击我启动.bat
================================================
@echo off
if not exist %windir%\system32\cmd.exe (
    "%CD%\zfile\zfile.exe"
) else (
    cmd /k "%CD%\zfile\zfile.exe"
    exit
)

================================================
FILE: Dockerfile
================================================
# 此文件仅作为示例使用,与 ZFile 实际打包的 Dockerfile 不同(采用 Graal Native 打包,这部分不开源)
FROM maven:3.9.9-eclipse-temurin-21-alpine AS builder

WORKDIR /root

ADD ./pom.xml pom.xml
ADD ./src src

RUN mvn clean package -Dmaven.test.skip=true

FROM ibm-semeru-runtimes:open-21-jre-jammy

WORKDIR /root
EXPOSE 8080

ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

RUN apt update -y && apt install --no-install-recommends fontconfig -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

COPY --from=builder /root/target/*.jar /root/app.jar

CMD ["java", "-jar", "app.jar"]

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 ZhaoJun

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<div align="center">
    <a href="https://zfile.vip" target="_blank" rel="noopener noreferrer">
        <img style="margin: auto; width: 100px; display: block" src="/img/logo-zfile.png" alt="ZFile" />
    </a>
    <p>ZFile 是一个适用于个人或小团队的在线网盘程序,可以将多种存储类型统一管理,再也不用登录各种网站管理文件,现在你只需要在 ZFile 中畅快使用!</p>
<div>
    <img alt="last commit"      src="https://shields.io/github/last-commit/zfile-dev/zfile.svg?style=flat-square"/>
    <img alt="downloads"        src="https://shields.io/github/downloads/zfile-dev/zfile/total?style=flat-square"/>
    <img alt="release version"  src="https://shields.io/github/v/release/zfile-dev/zfile?style=flat-square"/>
    <img alt="commit activity"  src="https://shields.io/github/commit-activity/y/zfile-dev/zfile?style=flat-square"/>
    <img alt="open issues"      src="https://shields.io/github/issues/zfile-dev/zfile?style=flat-square"/>
    <img alt="closed issues"    src="https://shields.io/github/issues-closed-raw/zfile-dev/zfile?style=flat-square"/>
    <img alt="forks"            src="https://shields.io/github/forks/zfile-dev/zfile?style=flat-square"/>
    <img alt="stars"            src="https://shields.io/github/stars/zfile-dev/zfile?style=flat-square"/>
    <img alt="watchers"         src="https://shields.io/github/watchers/zfile-dev/zfile?style=flat-square"/>
    <img alt="gitcode"          src="https://gitcode.com/zfile-dev/zfile/star/badge.svg"/>
</div>
    <span>
        <a href="https://zfile.vip">官网</a>
        <span> | </span>
        <a href="https://docs.zfile.vip">文档</a>
        <span> | </span>
        <a href="https://demo.zfile.vip">预览地址</a>
    </span>
</div>

## 系统特色

- Docker、Docker Compose 支持(amd64, arm64)。
- 支持对文件生成直链、短链(可设过期时间)。
- 响应式设计,支持手机、平板、电脑等多种设备访问。
- 支持多用户功能,可分配给指定用户指定存储源或目录。
- 支持在线浏览图片、播放音视频,文本文件、Office、Obj(3d)等文件类型。
- 支持对接 S3、OneDrive、SharePoint、Google Drive、多吉云、又拍云、本地存储、FTP、SFTP 等存储源。
- 支持常用快捷键,`Ctrl + A` 全选,`Ctrl + 左键` 多选,`Shift + 左键` 范围选择,`Esc` 取消全选等。
- 支持限速下载(捐赠版)
- 支持限制指定用户可查看、上传的文件类型(捐赠版)

## 快速开始

一键脚本安装:

```bash
curl -sSL https://docs.zfile.vip/install.sh -o install.sh && chmod +x install.sh && ./install.sh
```

更多安装方式请参考 [安装文档](https://docs.zfile.vip/install/)


## 功能预览

### 文件列表
![文件列表](/img/file-list.png)
### 画廊模式
![图片预览](/img/gallery.png)
### 视频预览
![视频预览](/img/preview-video.png)
### 文本预览
![文本预览](/img/preview-text.png)
### 音频预览
![音频预览](/img/preview-audio.png)
### PDF 预览
![PDF 预览](/img/preview-pdf.png)
### Office 预览
![Office 预览](/img/preview-office.png)
### 3d 文件预览
![3d 文件预览](/img/preview-3d.png)
### 生成直链
![生成直链](/img/generate-link.jpeg)
### 页面设置
![页面设置](/img/page-setting.png)
### 后台设置-登录
![后台设置-登录](/img/login.png)
### 后台设置-存储源列表
![后台设置-存储源列表](/img/storage-list.png)
### 后台设置-添加存储源(本地存储)
![后台设置-添加存储源(本地存储)](/img/storage-edit-local.png)
### 后台设置-用户管理
![后台设置-存储源权限控制](/img/user-edit.png)
### 后台设置-显示设置
![后台设置-显示设置](/img/view-setting.png)

## 支持作者

如果本项目对你有帮助,请作者喝杯咖啡吧。

<img src="https://cdn.jun6.net/2021/03/27/152704e91f13d.png" width="400" alt="赞助我">

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=zfile-dev/zfile&type=Date)](https://star-history.com/#zfile-dev/zfile&Date)


================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>im.zhaojun</groupId>
    <artifactId>zfile</artifactId>
    <version>4.5.0</version>
    <name>zfile</name>
    <packaging>jar</packaging>
    <description>一个在线的文件浏览系统</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <skipTests>true</skipTests>

        <java.version>21</java.version>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>

        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
        <snakeyaml.version>2.0</snakeyaml.version>
        <jackson-bom.version>2.14.1</jackson-bom.version>
        <sqlite-jdbc.version>3.46.0.1</sqlite-jdbc.version>
        <flyway.version>10.12.0</flyway.version>

        <lombok.version>1.18.32</lombok.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>bom</artifactId>
                <version>2.24.3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!--         spring boot 官方相关-->
        <dependency>
            <groupId>org.graalvm.sdk</groupId>
            <artifactId>graal-sdk</artifactId>
            <version>24.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!-- 数据库相关 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
            <version>${flyway.version}</version>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-mysql</artifactId>
            <version>${flyway.version}</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.6</version>
        </dependency>


        <!-- 存储策略相关 API, 对象存储、FTP、 Rest API-->
        <dependency>
            <groupId>com.upyun</groupId>
            <artifactId>java-sdk</artifactId>
            <version>4.2.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/software.amazon.awssdk/s3 -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
        </dependency>
        <dependency>
            <groupId>com.qiniu</groupId>
            <artifactId>qiniu-java-sdk</artifactId>
            <version>7.12.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.mwiede</groupId>
            <artifactId>jsch</artifactId>
            <version>0.2.20</version>
        </dependency>
        <dependency>
            <groupId>com.github.lookfirst</groupId>
            <artifactId>sardine</artifactId>
            <version>5.12</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-simple</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 登陆/权限相关 -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot3-starter</artifactId>
            <version>1.38.0</version>
        </dependency>


        <!-- 文档相关 -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
            <version>4.5.0</version>
        </dependency>

        <!-- 工具类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.31</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.4.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.commons</groupId>
                    <artifactId>commons-compress</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.26.2</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.29</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>33.3.0-jre</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-chain</groupId>
            <artifactId>commons-chain</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>dev.samstevens.totp</groupId>
            <artifactId>totp</artifactId>
            <version>1.7.1</version>
            <exclusions>
                <exclusion>
                    <groupId>com.google.zxing</groupId>
                    <artifactId>core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.google.zxing</groupId>
                    <artifactId>javase</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20231013</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
            <version>4.5.13</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents.client5</groupId>
            <artifactId>httpclient5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.70</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.6.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dns-cache-manipulator</artifactId>
            <version>1.8.2</version>
        </dependency>

        <dependency>
            <groupId>com.github.oshi</groupId>
            <artifactId>oshi-core</artifactId>
            <version>6.6.3</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>21</source>
                    <target>21</target>
                    <encoding>UTF-8</encoding>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.32</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>0.2.0</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.flywaydb</groupId>
                <artifactId>flyway-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>


================================================
FILE: src/main/java/im/zhaojun/zfile/ZfileApplication.java
================================================
package im.zhaojun.zfile;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;


/**
 * @author zhaojun
 */
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
@ServletComponentScan(basePackages = {"im.zhaojun.zfile.core.filter", "im.zhaojun.zfile.module.storage.filter"})
@ComponentScan(basePackages = "im.zhaojun.zfile.*")
public class ZfileApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZfileApplication.class, args);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/annotation/ApiLimit.java
================================================
package im.zhaojun.zfile.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * 接口限流注解
 *
 * @author zhaojun
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiLimit {

    /**
     * 持续时间
     */
    int timeout();

    /**
     * 时间单位, 默认为秒
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

    /**
     * 单位时间内允许访问的最大次数
     */
    long maxCount();

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/annotation/DemoDisable.java
================================================
package im.zhaojun.zfile.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 演示系统禁用功能注解
 *
 * @author zhaojun
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DemoDisable {

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/aspect/ApiLimitAspect.java
================================================
package im.zhaojun.zfile.core.aspect;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.extra.servlet.JakartaServletUtil;
import im.zhaojun.zfile.core.annotation.ApiLimit;
import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;
import im.zhaojun.zfile.core.util.RequestHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 接口限流切面, 通过注解 {@link ApiLimit} 进行限流.
 *
 * @author zhaojun
 */
@Aspect
@Component
public class ApiLimitAspect {

    private final TimedCache<String, AtomicLong> apiLimitTimedCache = CacheUtil.newTimedCache(1000);

    public static final String API_LIMIT_KEY_PREFIX = "api_limit_";

    /**
     * 在标记了 {@link ApiLimit} 注解的方法执行前进行限流校验.
     *
     * @param joinPoint 切点
     */
    @Before("@annotation(apiLimit)")
    public void before(JoinPoint joinPoint, ApiLimit apiLimit) {
        // 获取当前请求的方法上的注解中设置的值
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 反射获取当前被调用的方法
        Method method = signature.getMethod();
        int timeout = apiLimit.timeout();
        TimeUnit timeUnit = apiLimit.timeUnit();
        long millis = timeUnit.toMillis(timeout);
        long maxCount = apiLimit.maxCount();

        // 获取请求相关信息
        String ip = JakartaServletUtil.getClientIP(RequestHolder.getRequest());

        // 限制访问次数
        String key = API_LIMIT_KEY_PREFIX.concat(ip).concat(method.getName());
        AtomicLong atomicLong = apiLimitTimedCache.get(key, false);
        if (atomicLong == null) {
            apiLimitTimedCache.put(key, new AtomicLong(1), millis);
        } else {
            if (atomicLong.incrementAndGet() > maxCount) {
                throw new BizException(ErrorCode.BIZ_ACCESS_TOO_FREQUENT);
            }
        }
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/aspect/CommonResultControllerAdvice.java
================================================
package im.zhaojun.zfile.core.aspect;

import im.zhaojun.zfile.core.constant.MdcConstant;
import im.zhaojun.zfile.core.util.AjaxJson;
import org.slf4j.MDC;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * Controller 切面, 用于处理返回值统一封装.
 *
 * @author zhaojun
 */
@ControllerAdvice
public class CommonResultControllerAdvice implements ResponseBodyAdvice<Object> {

	@Override
	public boolean supports(MethodParameter returnType,
							@NonNull Class<? extends HttpMessageConverter<?>> converterType) {
		return AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
	}

	@Override
	@NonNull
	public final Object beforeBodyWrite(@Nullable Object body,
										@NonNull MethodParameter returnType,
										@NonNull MediaType contentType,
										@NonNull Class<? extends HttpMessageConverter<?>> converterType,
										@NonNull ServerHttpRequest request,
										@NonNull ServerHttpResponse response) {
		MappingJacksonValue container = getOrCreateContainer(body);
		// The contain body will never be null
		beforeBodyWriteInternal(container, contentType, returnType, request, response);
		return container;
	}

	/**
	 * Wrap the body in a {@link MappingJacksonValue} value container (for providing
	 * additional serialization instructions) or simply cast it if already wrapped.
	 */
	private MappingJacksonValue getOrCreateContainer(Object body) {
		return body instanceof MappingJacksonValue ? (MappingJacksonValue) body :
				new MappingJacksonValue(body);
	}

	private void beforeBodyWriteInternal(MappingJacksonValue bodyContainer,
										 MediaType contentType,
										 MethodParameter returnType,
										 ServerHttpRequest request,
										 ServerHttpResponse response) {
		// Get return body
		Object returnBody = bodyContainer.getValue();

		if (returnBody instanceof AjaxJson<?> baseResponse) {
            // 将 MDC 中的 TraceId 设置到返回值中
			baseResponse.setTraceId(MDC.get(MdcConstant.TRACE_ID));
		}
	}

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/aspect/DemoDisableAspect.java
================================================
package im.zhaojun.zfile.core.aspect;

import im.zhaojun.zfile.core.annotation.DemoDisable;
import im.zhaojun.zfile.core.config.ZFileProperties;
import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;
import jakarta.annotation.Resource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 通过注解 {@link DemoDisable} 限制演示系统不可操作的功能.
 *
 * @author zhaojun
 */
@Aspect
@Component
public class DemoDisableAspect {

    @Resource
    private ZFileProperties zFileProperties;

    /**
     * 定义一个切点(通过注解)
     */
    @Pointcut("@annotation(im.zhaojun.zfile.core.annotation.DemoDisable)")
    public void demoDisable() {
    }

    /**
     * 在标记了 {@link DemoDisable} 注解的方法执行前进行限流校验.
     *
     * @param joinPoint 切点
     */
    @Before("demoDisable()")
    public void before(JoinPoint joinPoint) {
        if (zFileProperties.isDemoSite()) {
            throw new BizException(ErrorCode.DEMO_SITE_DISABLE_OPERATOR);
        }
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/cache/ZFileCacheManager.java
================================================
package im.zhaojun.zfile.core.cache;

import im.zhaojun.zfile.module.storage.model.entity.StorageSource;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
 * ZFile 业务缓存,针对无法使用 jsr-107 的缓存进行处理的业务逻辑。
 */
@Component
public class ZFileCacheManager {

    /**
     * 用户可用的存储源列表缓存
     */
    private static final Map<Integer, List<StorageSource>> userEnableStorageSourceCache = new ConcurrentHashMap<>();

    /**
     * 根据用户 ID 获取可用的存储源列表,若缓存中不存在,则通过 mappingFunction 获取并返回。
     *
     * @param   userId
     *          用户 ID
     *
     * @param   mappingFunction
     *          当缓存中不存在时,用于获取存储源列表的函数。
     *
     * @return  存储源列表函数
     */
    public List<StorageSource> findAllEnableOrderByOrderNum(Integer userId, Function<Integer, List<StorageSource>> mappingFunction) {
        return userEnableStorageSourceCache.computeIfAbsent(userId, mappingFunction);
    }

    /**
     * 清空所有用户的存储源缓存。
     */
    public void clearUserEnableStorageSourceCache() {
        userEnableStorageSourceCache.clear();
    }

    /**
     * 清除指定用户的存储源缓存。
     *
     * @param   userId
     *          用户 ID
     */
    public void clearUserEnableStorageSourceCache(Integer userId) {
        userEnableStorageSourceCache.remove(userId);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/ZFileProperties.java
================================================
package im.zhaojun.zfile.core.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * ZFile 配置类,将配置文件中的 zfile 配置项映射到该类中.
 *
 * @author zhaojun
 */
@Data
@EnableConfigurationProperties
@Component
@ConfigurationProperties(prefix = "zfile")
public class ZFileProperties {

	private boolean debug;

	private String version;

	private boolean isDemoSite;

	private OAuth2Properties onedrive = new OAuth2Properties();
	private OAuth2Properties onedriveChina = new OAuth2Properties();
	private OAuth2Properties gd = new OAuth2Properties();
	private Open115Properties open115 = new Open115Properties();

	@Data
	public static class OAuth2Properties {
		private String clientId;
		private String clientSecret;
		private String redirectUri;
		private String scope;
	}

	@Data
	public static class Open115Properties {
		private String appId;
	}

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/datasource/DataSourceBeanPostProcessor.java
================================================
package im.zhaojun.zfile.core.config.datasource;


import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.zaxxer.hikari.HikariDataSource;
import im.zhaojun.zfile.core.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.flyway.FlywayProperties;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.List;

/**
 * 在 Spring 容器初始化时, 对数据源进行处理.
 * <br/>
 * 1. 针对 DataSource 进行处理,仅针对 sqlite:
 * <ul>
 *     <li>提前创建 sqlite 数据文件所在目录.</li>
 *     <li>检测到版本更新时(pom.xml -> project.version)自动备份原数据库.</li>
 * </ul>
 * <br/>
 * 2. 针对 Flyway 进行处理,根据数据库类型, 配置不同的 Flyway Migration Location:
 * <ul>
 *     <li>SQLite 数据库使用 migration-sqlite 目录.</li>
 *     <li>MySQL 数据库使用 migration-mysql 目录.</li>
 * </ul>
 *
 * @author zhaojun
 */
@Slf4j
@Component
public class DataSourceBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {

    public static final String ZFILE_VERSION_PROPERTIES = "zfile.db.version";

    public static final String DRIVE_CLASS_NAME_PROPERTIES = "spring.datasource.driver-class-name";

    public static final String DATA_SOURCE_BEAN_NAME = "dataSource";

    public static final String SQLITE_DRIVE_CLASS_NAME = "org.sqlite.JDBC";

    public static final String MYSQL_DRIVE_CLASS_NAME = "com.mysql.cj.jdbc.Driver";

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 如果更改了数据源类型这里要修改
        if (bean instanceof HikariDataSource dataSource && DATA_SOURCE_BEAN_NAME.equals(beanName)) {
            processSqliteDataSource(dataSource);
        } else if (bean instanceof FlywayProperties flywayProperties) {
            processFlywayLocations(flywayProperties);
        }
        return bean;
    }

    /**
     * 如果是 sqlite 数据库, 提前创建数据库文件所在目录. <br/>
     *
     * 如果检测到版本更新, 自动备份原数据库文件.
     *
     * @param   dataSource
     *          数据源
     */
    private void processSqliteDataSource(HikariDataSource dataSource) {
        String driverClassName = dataSource.getDriverClassName();
        String jdbcUrl = dataSource.getJdbcUrl();
        if (StringUtils.equals(driverClassName, SQLITE_DRIVE_CLASS_NAME)) {
            String path = jdbcUrl.replace("jdbc:sqlite:", "");
            String folderPath = FileUtil.getAbsolutePath(new File(path).getParentFile());
            log.info("SQLite 数据库文件所在目录: [{}]", folderPath);
            File file = new File(folderPath);
            if (!file.exists()) {
                log.info("检测到 SQLite 数据库文件所在目录不存在, 已自动创建.");
                if (!file.mkdirs()) {
                    log.error("SQLite 数据库文件创建失败.");
                }
            } else {
                log.info("检测到 SQLite 数据库文件所在目录已存在, 无需自动创建.");

                // 更新版本时, 先自动备份数据库文件
                String version = SpringUtil.getProperty(ZFILE_VERSION_PROPERTIES);
                if (StringUtils.isNotEmpty(version)) {
                    String backupPath = folderPath + "/zfile-update-" + version + "-backup.db";
                    if (!FileUtil.exist(path)) {
                        log.error("检测到 SQLite 数据库文件不存在, 一般为初始化状态,无需备份.");
                        return;
                    }
                    if (FileUtil.exist(backupPath)) {
                        log.info("检测到 SQLite 数据库备份文件 [{}] 已存在, 无需再次备份.", backupPath);
                    } else {
                        FileUtil.copy(path, backupPath, false);
                        log.info("自动备份 SQLite 数据库文件到: [{}]", backupPath);
                    }
                }
            }
        }
    }

    /**
     * 根据使用的不同数据库, 配置使用不同的 migration location
     *
     * @param   flywayProperties
     *          flyway 配置项
     */
    private void processFlywayLocations(FlywayProperties flywayProperties) {
        String driveClassName = SpringUtil.getProperty(DRIVE_CLASS_NAME_PROPERTIES);
        if (SQLITE_DRIVE_CLASS_NAME.equals(driveClassName)) {
            flywayProperties.setLocations(List.of("classpath:db/migration-sqlite"));
        } else if (MYSQL_DRIVE_CLASS_NAME.equals(driveClassName)) {
            flywayProperties.setLocations(List.of("classpath:db/migration-mysql"));
        }
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/docs/Knife4jConfiguration.java
================================================
package im.zhaojun.zfile.core.config.docs;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.HeaderParameter;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Knife4j 参数配置,区分前台功能和管理员功能,并为管理员接口增加统一 token header 配置.
 *
 * @author zhaojun
 */
@Configuration
public class Knife4jConfiguration {

    @Bean
    public GroupedOpenApi groupedOpenApi() {
        String groupName = "前台功能";
        return GroupedOpenApi.builder()
                .group(groupName)
                .packagesToScan("im.zhaojun.zfile.module")
                .pathsToExclude("/admin/**")
                .build();
    }

    @Bean
    public GroupedOpenApi groupedOpenApi2() {
        String groupName = "管理员功能";
        return GroupedOpenApi.builder()
                .group(groupName)
                .packagesToScan("im.zhaojun.zfile.module")
                .pathsToMatch("/admin/**")
                .addOperationCustomizer(globalOperationCustomizer())
                .build();
    }

    public OperationCustomizer globalOperationCustomizer() {
        return (operation, handlerMethod) -> {
            operation.addParametersItem(new HeaderParameter()
                    .name("zfile-token")
                    .description("token")
                    .required(true)
                    .schema(new StringSchema()));
            return operation;
        };
    }

    @Bean
    public OpenAPI customOpenAPI() {
        Contact contact = new Contact();
        contact.setName("zhaojun");
        contact.setUrl("https://zfile.vip");
        contact.setEmail("873019219@qq.com");

        return new OpenAPI()
                .info(new Info()
                        .title("ZFILE 文档")
                        .description("# 这是 ZFILE Restful 接口文档展示页面")
                        .termsOfService("https://www.zfile.vip")
                        .contact(contact)
                        .version("1.0")
                        .license(new License()
                                .name("Apache 2.0")
                                .url("http://doc.xiaominfo.com")));
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/jackson/JSONStringDeserializer.java
================================================
package im.zhaojun.zfile.core.config.jackson;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

/**
 * JSON String 反序列化器, 用于将 JSON 字符串反序列化为 JSON 对象.
 *
 * @author zhaojun
 */
public class JSONStringDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser p, DeserializationContext context) throws IOException {
        JsonNode node = p.getCodec().readTree(p);
        ObjectMapper mapper = new ObjectMapper();
        return mapper.writeValueAsString(node);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/jackson/JSONStringSerializer.java
================================================
package im.zhaojun.zfile.core.config.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;


/**
 * JSON String 序列化器, 用于将 JSON 字符串序列化为 JSON 对象.
 *
 * @author zhaojun
 */
public class JSONStringSerializer extends JsonSerializer<String> {

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeRawValue(value);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/CollectionIntegerTypeHandler.java
================================================
package im.zhaojun.zfile.core.config.mybatis;

import java.util.Set;

public class CollectionIntegerTypeHandler extends CollectionTypeHandler<Set<Integer>> {

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/CollectionStrTypeHandler.java
================================================
package im.zhaojun.zfile.core.config.mybatis;

import java.util.Set;

public class CollectionStrTypeHandler extends CollectionTypeHandler<Set<String>> {

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/CollectionTypeHandler.java
================================================
package im.zhaojun.zfile.core.config.mybatis;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.springframework.core.ResolvableType;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * 自定义 Set 类型处理器, 用于处理数据库 VARCHAR 类型字段和 Java Set 类型属性之间的转换.
 * 支持字符串格式为: "[a, b, c]".
 *
 * @author zhaojun
 */
@MappedJdbcTypes(JdbcType.VARCHAR)
public abstract class CollectionTypeHandler<T> extends BaseTypeHandler<Object> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
            throws SQLException {
        if (parameter instanceof Collection collection) {
            StringJoiner joiner = new StringJoiner(",");
            for (Object o : collection) {
                joiner.add(Convert.toStr(o));
            }
            ps.setString(i, joiner.toString());
        } else {
            ps.setString(i, Convert.toStr(parameter));
        }
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName)
            throws SQLException {
        String str = rs.getString(columnName);
        return convertToEntityAttribute(str);
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex)
            throws SQLException {
        String str = rs.getString(columnIndex);
        return convertToEntityAttribute(str);
    }

    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        String str = cs.getString(columnIndex);
        return convertToEntityAttribute(str);
    }

    private Class<?> collectionClazz;

    private Type innerType;

    /**
     * 构造方法
     */
    public CollectionTypeHandler() {
        ResolvableType resolvableType = ResolvableType.forClass(getClass());
        Type type = resolvableType.as(CollectionTypeHandler.class).getGeneric().getType();

        if (type instanceof ParameterizedType parameterizedType) {
            collectionClazz = (Class<?>) parameterizedType.getRawType();
            // 获取实际类型参数(泛型参数,例如 List<String> 中的 String)
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

            // 使用这些信息做进一步操作
            for (Type actualTypeArgument : actualTypeArguments) {
                innerType = actualTypeArgument;
                break;
            }
        }
    }

    private Object convertToEntityAttribute(String dbData) {
        if (StrUtil.isEmpty(dbData)) {
            if (List.class.isAssignableFrom(collectionClazz)) {
                return Collections.emptyList();
            } else if (Set.class.isAssignableFrom(collectionClazz)) {
                return Collections.emptySet();
            } else {
                return null;
            }
        }

        Collection collection;

        if (List.class.isAssignableFrom(collectionClazz)) {
            collection = new ArrayList<>();
        } else if (Set.class.isAssignableFrom(collectionClazz)) {
            collection = new HashSet<>();
        } else {
            return null;
        }

        String[] split = dbData.split(",");
        for (String s : split) {
            if (NumberUtil.isNumber(s)) {
                collection.add(Convert.convert(Integer.class, s));
            } else {
                collection.add(s);
            }
        }

        return collection;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/MyBatisPlusConfig.java
================================================
package im.zhaojun.zfile.core.config.mybatis;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * mybatis-plus 配置类
 *
 * @author zhaojun
 */
@Configuration
public class MyBatisPlusConfig {

    /**
     * mybatis plus 分页插件配置
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(DataSource dataSource) throws SQLException {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        String databaseProductName = dataSource.getConnection().getMetaData().getDatabaseProductName();
        DbType dbType = DbType.getDbType(databaseProductName);
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(dbType));
        return interceptor;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/MyDatabaseIdProvider.java
================================================
package im.zhaojun.zfile.core.config.mybatis;

import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * MyBatis 数据库 ID Provider, 用于判断当前数据库类型来执行不同的 SQL 语句. <br>
 * 可在 xml 中使用 <code>&lt;if test="_databaseId = 'mysql'"&gt; </code> 来判断数据库类型. <br>
 * 也可以在外层使用,如 <code>&lt;delete id="xxx" databaseId="sqlite"&gt;</code> 来判断数据库类型.
 *
 * @author zhaojun
 */
@Component
public class MyDatabaseIdProvider implements DatabaseIdProvider {

    private static final String DATABASE_MYSQL = "MySQL";
    private static final String DATABASE_SQLITE = "SQLite";

    @Override
    public String getDatabaseId(DataSource dataSource) throws SQLException {
        Connection conn = dataSource.getConnection();
        String dbName = conn.getMetaData().getDatabaseProductName();
        String dbAlias = "";
        switch (dbName) {
            case DATABASE_MYSQL:
                dbAlias = "mysql";
                break;
            case DATABASE_SQLITE:
                dbAlias = "sqlite";
                break;
            default:
                break;
        }
        return dbAlias;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/MyMetaObjectHandler.java
================================================
package im.zhaojun.zfile.core.config.mybatis;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * MyBatis Plus 自动填充配置类
 * 用于自动填充 createTime 和 updateTime 字段
 *
 * @author zhaojun
 */
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/security/SaSessionForJacksonCustomized.java
================================================
package im.zhaojun.zfile.core.config.security;

import cn.dev33.satoken.session.SaSession;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

/**
 * Jackson 定制版 SaSession,忽略 timeout 等属性的序列化
 *
 * @author click33
 * @since 1.34.0
 */
@JsonIgnoreProperties({"timeout"})
public class SaSessionForJacksonCustomized extends SaSession {

    /**
     *
     */
    private static final long serialVersionUID = -7600983549653130681L;

    public SaSessionForJacksonCustomized() {
        super();
    }

    /**
     * 构建一个Session对象
     * @param id Session的id
     */
    public SaSessionForJacksonCustomized(String id) {
        super(id);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/security/SaTokenConfigure.java
================================================
package im.zhaojun.zfile.core.config.security;

import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.strategy.SaStrategy;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * SaToken 权限配置, 配置管理员才能访问管理员功能.
 *
 * @author zhaojun
 */
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

    /**
     * 注册权限校验拦截器, 拦截所有 /admin/** 请求,但不包含 /admin 因为这个是登录页面.
     *
     * @param   registry
     *          拦截器注册器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SaInterceptor(handle -> {
            SaRouter.match("/admin/**", () -> {
                StpUtil.checkLogin();
                StpUtil.checkRole("admin");
            });
        })).addPathPatterns("/**").excludePathPatterns("/admin");

        // 不再依赖 SaToken 的默认路径检查功能
        SaStrategy.instance.checkRequestPath = (path, extArg1, extArg2) -> {};
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/security/SaTokenDaoRedisJackson.java
================================================
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package im.zhaojun.zfile.core.config.security;

import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Sa-Token 持久层实现 [ Redis存储、Jackson序列化 ]
 *
 * @author click33
 * @since 1.34.0
 */
@Component
@ConditionalOnProperty(name = "spring.data.redis.host")
public class SaTokenDaoRedisJackson implements SaTokenDao {

    public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_PATTERN = "yyyy-MM-dd";
    public static final String TIME_PATTERN = "HH:mm:ss";
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
    public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);
    public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(TIME_PATTERN);

    /**
     * ObjectMapper 对象 (以 public 作用域暴露出此对象,方便开发者二次更改配置)
     *
     * <p> 例如:
     * 	<pre>
     *      SaTokenDaoRedisJackson redisJackson = (SaTokenDaoRedisJackson) SaManager.getSaTokenDao();
     *      redisJackson.objectMapper.xxx = xxx;
     * 	</pre>
     * </p>
     */
    public ObjectMapper objectMapper;

    /**
     * String 读写专用
     */
    public StringRedisTemplate stringRedisTemplate;

    /**
     * Object 读写专用
     */
    public RedisTemplate<String, Object> objectRedisTemplate;

    /**
     * 标记:是否已初始化成功
     */
    public boolean isInit;

    @Autowired
    public void init(RedisConnectionFactory connectionFactory) {
        // 如果已经初始化成功了,就立刻退出,不重复初始化
        if(this.isInit) {
            return;
        }

        // 指定相应的序列化方案
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();

        // 通过反射获取Mapper对象, 增加一些配置, 增强兼容性
        try {
            Field field = GenericJackson2JsonRedisSerializer.class.getDeclaredField("mapper");
            field.setAccessible(true);
            this.objectMapper = (ObjectMapper) field.get(valueSerializer);

            // 配置[忽略未知字段]
            this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

            // 配置[时间类型转换]
            JavaTimeModule timeModule = new JavaTimeModule();

            // LocalDateTime序列化与反序列化
            timeModule.addSerializer(new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
            timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER));

            // LocalDate序列化与反序列化
            timeModule.addSerializer(new LocalDateSerializer(DATE_FORMATTER));
            timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER));

            // LocalTime序列化与反序列化
            timeModule.addSerializer(new LocalTimeSerializer(TIME_FORMATTER));
            timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(TIME_FORMATTER));

            this.objectMapper.registerModule(timeModule);

            // 重写 SaSession 生成策略
            SaStrategy.instance.createSession = (sessionId) -> new SaSessionForJacksonCustomized(sessionId);
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        // 构建StringRedisTemplate
        StringRedisTemplate stringTemplate = new StringRedisTemplate();
        stringTemplate.setConnectionFactory(connectionFactory);
        stringTemplate.afterPropertiesSet();

        // 构建RedisTemplate
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(keySerializer);
        template.setHashKeySerializer(keySerializer);
        template.setValueSerializer(valueSerializer);
        template.setHashValueSerializer(valueSerializer);
        template.afterPropertiesSet();

        // 开始初始化相关组件
        this.stringRedisTemplate = stringTemplate;
        this.objectRedisTemplate = template;

        // 打上标记,表示已经初始化成功,后续无需再重新初始化
        this.isInit = true;
    }


    /**
     * 获取Value,如无返空
     */
    @Override
    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 写入Value,并设定存活时间 (单位: 秒)
     */
    @Override
    public void set(String key, String value, long timeout) {
        if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE)  {
            return;
        }
        // 判断是否为永不过期
        if(timeout == SaTokenDao.NEVER_EXPIRE) {
            stringRedisTemplate.opsForValue().set(key, value);
        } else {
            stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
        }
    }

    /**
     * 修修改指定key-value键值对 (过期时间不变)
     */
    @Override
    public void update(String key, String value) {
        long expire = getTimeout(key);
        // -2 = 无此键
        if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
            return;
        }
        this.set(key, value, expire);
    }

    /**
     * 删除Value
     */
    @Override
    public void delete(String key) {
        stringRedisTemplate.delete(key);
    }

    /**
     * 获取Value的剩余存活时间 (单位: 秒)
     */
    @Override
    public long getTimeout(String key) {
        return stringRedisTemplate.getExpire(key);
    }

    /**
     * 修改Value的剩余存活时间 (单位: 秒)
     */
    @Override
    public void updateTimeout(String key, long timeout) {
        // 判断是否想要设置为永久
        if(timeout == SaTokenDao.NEVER_EXPIRE) {
            long expire = getTimeout(key);
            if(expire == SaTokenDao.NEVER_EXPIRE) {
                // 如果其已经被设置为永久,则不作任何处理
            } else {
                // 如果尚未被设置为永久,那么再次set一次
                this.set(key, this.get(key), timeout);
            }
            return;
        }
        stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }



    /**
     * 获取Object,如无返空
     */
    @Override
    public Object getObject(String key) {
        return objectRedisTemplate.opsForValue().get(key);
    }

    /**
     * 写入Object,并设定存活时间 (单位: 秒)
     */
    @Override
    public void setObject(String key, Object object, long timeout) {
        if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE)  {
            return;
        }
        // 判断是否为永不过期
        if(timeout == SaTokenDao.NEVER_EXPIRE) {
            objectRedisTemplate.opsForValue().set(key, object);
        } else {
            objectRedisTemplate.opsForValue().set(key, object, timeout, TimeUnit.SECONDS);
        }
    }

    /**
     * 更新Object (过期时间不变)
     */
    @Override
    public void updateObject(String key, Object object) {
        long expire = getObjectTimeout(key);
        // -2 = 无此键
        if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
            return;
        }
        this.setObject(key, object, expire);
    }

    /**
     * 删除Object
     */
    @Override
    public void deleteObject(String key) {
        objectRedisTemplate.delete(key);
    }

    /**
     * 获取Object的剩余存活时间 (单位: 秒)
     */
    @Override
    public long getObjectTimeout(String key) {
        return objectRedisTemplate.getExpire(key);
    }

    /**
     * 修改Object的剩余存活时间 (单位: 秒)
     */
    @Override
    public void updateObjectTimeout(String key, long timeout) {
        // 判断是否想要设置为永久
        if(timeout == SaTokenDao.NEVER_EXPIRE) {
            long expire = getObjectTimeout(key);
            if(expire == SaTokenDao.NEVER_EXPIRE) {
                // 如果其已经被设置为永久,则不作任何处理
            } else {
                // 如果尚未被设置为永久,那么再次set一次
                this.setObject(key, this.getObject(key), timeout);
            }
            return;
        }
        objectRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }


    /**
     * 搜索数据
     */
    @Override
    public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
        Set<String> keys = stringRedisTemplate.keys(prefix + "*" + keyword + "*");
        List<String> list = new ArrayList<>(keys);
        return SaFoxUtil.searchList(list, start, size, sortType);
    }

}


================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/security/StpInterfaceImpl.java
================================================
package im.zhaojun.zfile.core.config.security;

import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.convert.Convert;
import im.zhaojun.zfile.module.user.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;

/**
 * 自定义权限加载接口实现类
 *
 * @author zhaojun
 */
@Component
public class StpInterfaceImpl implements StpInterface {

    private static final List<String> ADMIN_ROLE_LIST = Collections.singletonList("admin");

    public static final List<String> EMPTY_ROLE_LIST = Collections.emptyList();

    @Resource
    private UserService userService;

    /**
     * 返回一个账号所拥有的权限码集合,这里没用到这个功能,所以返回空集合
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        return Collections.emptyList();
    }

    /**
     * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        boolean isAdmin = userService.isAdmin(Convert.toInt(loginId));
        return isAdmin ? ADMIN_ROLE_LIST : EMPTY_ROLE_LIST;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/spring/JacksonEnumDeserializer.java
================================================
package im.zhaojun.zfile.core.config.spring;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.jackson.JsonComponent;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * Jackson 枚举反序列化器, 用于将接收请求中的参数(一般为字符串)转换为枚举对象.
 *
 * @author zhaojun
 */
@Setter
@Slf4j
@JsonComponent
public class JacksonEnumDeserializer extends JsonDeserializer<Enum<?>> implements ContextualDeserializer {

	private Class<?> clazz;

    /**
     * 反序列化操作
     *
     * @param   jsonParser
     *          json 解析器
     *
     * @param   ctx
     *          反序列化上下文
     *
     * @return  反序列化后的枚举值
     * @throws  IOException  反序列化异常
	 */
	@Override
	public Enum<?> deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
		Class<?> enumType = clazz;
		if (Objects.isNull(enumType) || !enumType.isEnum()) {
			return null;
		}
		String text = jsonParser.getText();
		Method method = StringToEnumConverterFactory.getMethod(clazz);
		Enum<?>[] enumConstants = (Enum<?>[]) enumType.getEnumConstants();

		// 将值与枚举对象对应并缓存
		for (Enum<?> e : enumConstants) {
			try {
				if (Objects.equals(method.invoke(e).toString(), text)) {
					return e;
				}
			} catch (IllegalAccessException | InvocationTargetException ex) {
				log.error("获取枚举值错误!!! ", ex);
			}
		}
		return null;
	}


	/**
	 * 为不同的枚举获取合适的解析器
	 *
	 * @param   ctx
     *          反序列化上下文
     *
	 * @param   property
     *          property
	 */
	@Override
	public JsonDeserializer<Enum<?>> createContextual(DeserializationContext ctx, BeanProperty property) {
		Class<?> rawCls = ctx.getContextualType().getRawClass();
		JacksonEnumDeserializer converter = new JacksonEnumDeserializer();
		converter.setClazz(rawCls);
		return converter;
	}

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/spring/SpringCacheConfig.java
================================================
package im.zhaojun.zfile.core.config.spring;

import im.zhaojun.zfile.core.config.security.SaTokenDaoRedisJackson;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheManagerProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Spring Cache 相关配置
 *
 * @author zhaojun
 */
@Configuration
@EnableCaching
public class SpringCacheConfig {

	@Value("${zfile.dbCache.enable:true}")
	private Boolean dbCacheEnable;

	/**
	 * 使用 TransactionAwareCacheManagerProxy 装饰 ConcurrentMapCacheManager,使其支持事务 (将 put、evict、clear 操作延迟到事务成功提交再执行.)
	 */
	@Bean
	@ConditionalOnMissingBean(SaTokenDaoRedisJackson.class)
	public CacheManager cacheManager() {
		return BooleanUtils.isNotTrue(dbCacheEnable) ? new NoOpCacheManager() : new TransactionAwareCacheManagerProxy(new ConcurrentMapCacheManager());
	}
	
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/spring/StringToEnumConverterFactory.java
================================================
package im.zhaojun.zfile.core.config.spring;

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.baomidou.mybatisplus.annotation.IEnum;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import im.zhaojun.zfile.core.exception.core.SystemException;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * String 转枚举通用转换器工厂
 *
 * @author zhaojun
 */
@Slf4j
public class StringToEnumConverterFactory implements ConverterFactory<String, Enum<?>> {

    /**
     * 存储枚举类型的缓存
     */
    private static final Map<Class<?>, Converter<String, ? extends Enum<?>>> CONVERTER_MAP = new ConcurrentHashMap<>();

    /**
     * 枚举类的获取枚举值方法缓存
     */
    private static final Map<Class<?>, Method> TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>();

    @Override
    @SuppressWarnings("unchecked cast")
    public <T extends Enum<?>> Converter<String, T> getConverter(Class<T> targetType) {
        // 缓存转换器
        Converter<String, T> converter = (Converter<String, T>) CONVERTER_MAP.get(targetType);
        if (converter == null) {
            converter = new StringToEnumConverter<>(targetType);
            CONVERTER_MAP.put(targetType, converter);
        }
        return converter;
    }

    static class StringToEnumConverter<T extends Enum<?>> implements Converter<String, T> {

        private final Map<String, T> enumMap = new ConcurrentHashMap<>();

        StringToEnumConverter(Class<T> enumType) {
            Method method = getMethod(enumType);
            T[] enums = enumType.getEnumConstants();

            // 将值与枚举对象对应并缓存
            for (T e : enums) {
                try {
                    enumMap.put(method.invoke(e).toString(), e);
                } catch (IllegalAccessException | InvocationTargetException ex) {
                    log.error("获取枚举值错误!!! ", ex);
                }
            }
        }


        @Override
        public T convert(@NotNull String source) {
            // 获取
            T t = enumMap.get(source);
            if (t == null) {
                throw new SystemException("该字符串找不到对应的枚举对象 字符串:" + source);
            }
            return t;
        }
    }


    public static <T> Method getMethod(Class<T> enumType) {
        Method method;
        // 找到取值的方法
        if (IEnum.class.isAssignableFrom(enumType)) {
            try {
                method = enumType.getMethod("getValue");
            } catch (NoSuchMethodException e) {
                throw new SystemException(String.format("类:%s 找不到 getValue方法",
                        enumType.getName()));
            }
        } else {
            method = TABLE_METHOD_OF_ENUM_TYPES.computeIfAbsent(enumType, k -> {
                Field field =
                        dealEnumType(enumType).orElseThrow(() -> new IllegalArgumentException(String.format(
                                "类:%s 找不到 EnumValue注解", enumType.getName())));

                Class<?> fieldType = field.getType();
                String fieldName = field.getName();
                String methodName =  StringUtils.concatCapitalize(boolean.class.equals(fieldType) ? "is" : "get", fieldName);
                try {
                    return enumType.getDeclaredMethod(methodName);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
                return null;
            });
        }
        return method;
    }


    private static Optional<Field> dealEnumType(Class<?> clazz) {
        return clazz.isEnum() ?
                Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(EnumValue.class)).findFirst() : Optional.empty();
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/spring/WebMvcConfig.java
================================================
package im.zhaojun.zfile.core.config.spring;

import im.zhaojun.zfile.module.storage.model.enums.StorageTypeEnum;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * ZFile Web 相关配置.
 *
 * @author zhaojun
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * 支持 url 中传入 <>[\]^`{|} 这些特殊字符.
     */
    @Bean
    public ServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory webServerFactory = new TomcatServletWebServerFactory();

        // 添加对 URL 中特殊符号的支持.
        webServerFactory.addConnectorCustomizers(connector -> {
            connector.setProperty("relaxedPathChars", "<>[\\]^`{|}%[]");
            connector.setProperty("relaxedQueryChars", "<>[\\]^`{|}%[]");
        });
        return webServerFactory;
    }

    /**
     * 添加自定义枚举格式化器.
     * @see StorageTypeEnum
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new StringToEnumConverterFactory());
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/totp/TotpAutoConfiguration.java
================================================
package im.zhaojun.zfile.core.config.totp;

import dev.samstevens.totp.TotpInfo;
import dev.samstevens.totp.code.*;
import dev.samstevens.totp.qr.QrDataFactory;
import dev.samstevens.totp.secret.DefaultSecretGenerator;
import dev.samstevens.totp.secret.SecretGenerator;
import dev.samstevens.totp.time.SystemTimeProvider;
import dev.samstevens.totp.time.TimeProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass({TotpInfo.class})
@EnableConfigurationProperties({TotpProperties.class})
public class TotpAutoConfiguration {

    private final TotpProperties props;

    @Autowired
    public TotpAutoConfiguration(TotpProperties props) {
        this.props = props;
    }

    @Bean
    @ConditionalOnMissingBean
    public SecretGenerator secretGenerator() {
        int length = this.props.getSecret().getLength();
        return new DefaultSecretGenerator(length);
    }

    @Bean
    @ConditionalOnMissingBean
    public TimeProvider timeProvider() {
        return new SystemTimeProvider();
    }

    @Bean
    @ConditionalOnMissingBean
    public HashingAlgorithm hashingAlgorithm() {
        return HashingAlgorithm.SHA1;
    }

    @Bean
    @ConditionalOnMissingBean
    public QrDataFactory qrDataFactory(HashingAlgorithm hashingAlgorithm) {
        return new QrDataFactory(hashingAlgorithm, this.getCodeLength(), this.getTimePeriod());
    }

    @Bean
    @ConditionalOnMissingBean
    public CodeGenerator codeGenerator(HashingAlgorithm algorithm) {
        return new DefaultCodeGenerator(algorithm, this.getCodeLength());
    }

    @Bean
    @ConditionalOnMissingBean
    public CodeVerifier codeVerifier(CodeGenerator codeGenerator, TimeProvider timeProvider) {
        DefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
        verifier.setTimePeriod(this.getTimePeriod());
        verifier.setAllowedTimePeriodDiscrepancy(this.props.getTime().getDiscrepancy());
        return verifier;
    }

    private int getCodeLength() {
        return this.props.getCode().getLength();
    }

    private int getTimePeriod() {
        return this.props.getTime().getPeriod();
    }
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/config/totp/TotpProperties.java
================================================
package im.zhaojun.zfile.core.config.totp;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(
    prefix = "totp"
)
public class TotpProperties {
    private static final int DEFAULT_SECRET_LENGTH = 32;
    private static final int DEFAULT_CODE_LENGTH = 6;
    private static final int DEFAULT_TIME_PERIOD = 30;
    private static final int DEFAULT_TIME_DISCREPANCY = 1;
    private final Secret secret = new Secret();
    private final Code code = new Code();
    private final Time time = new Time();

    public TotpProperties() {
    }

    public Secret getSecret() {
        return this.secret;
    }

    public Code getCode() {
        return this.code;
    }

    public Time getTime() {
        return this.time;
    }

    public static class Time {
        private int period = 30;
        private int discrepancy = 1;

        public Time() {
        }

        public int getPeriod() {
            return this.period;
        }

        public void setPeriod(int period) {
            this.period = period;
        }

        public int getDiscrepancy() {
            return this.discrepancy;
        }

        public void setDiscrepancy(int discrepancy) {
            this.discrepancy = discrepancy;
        }
    }

    public static class Code {
        private int length = 6;

        public Code() {
        }

        public int getLength() {
            return this.length;
        }

        public void setLength(int length) {
            this.length = length;
        }
    }

    public static class Secret {
        private int length = 32;

        public Secret() {
        }

        public int getLength() {
            return this.length;
        }

        public void setLength(int length) {
            this.length = length;
        }
    }
}


================================================
FILE: src/main/java/im/zhaojun/zfile/core/constant/MdcConstant.java
================================================
package im.zhaojun.zfile.core.constant;

/**
 * Slf4j mdc 常量
 *
 * @author zhaojun
 */
public class MdcConstant {
	
	public static final String TRACE_ID = "traceId";
	
	public static final String IP = "ip";
	
	public static final String USER = "user";
	
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/constant/RuleTypeConstant.java
================================================
package im.zhaojun.zfile.core.constant;

/**
 * 规则表达式类型常量
 *
 * @author zhaojun
 */
public class RuleTypeConstant {

    public static final String IP = "ip";

    public static final String REGEX = "regex";

    public static final String ANT_PATH = "antPath";

    public static final String SPRING_SIMPLE = "springSimple";

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/constant/ZFileConstant.java
================================================
package im.zhaojun.zfile.core.constant;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
 * ZFile 常量
 *
 * @author zhaojun
 */
@Configuration
public class ZFileConstant {

    /**
     * 最大支持文本文件大小为 ? KB 的文件内容.
     */
    public static Long TEXT_MAX_FILE_SIZE_KB = 100L;

    @Autowired(required = false)
    public void setTextMaxFileSizeMb(@Value("${zfile.preview.text.maxFileSizeKb}") Long maxFileSizeKb) {
        ZFileConstant.TEXT_MAX_FILE_SIZE_KB = maxFileSizeKb;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/constant/ZFileHttpHeaderConstant.java
================================================
package im.zhaojun.zfile.core.constant;

/**
 * ZFile 自定义 HTTP 请求头常量
 *
 * @author zhaojun
 */
public class ZFileHttpHeaderConstant {

    public static final String ZFILE_TOKEN = "Zfile-Token";

    public static final String AXIOS_REQUEST = "Axios-Request";

    public static final String AXIOS_FROM = "Axios-From";

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/controller/FrontIndexController.java
================================================
package im.zhaojun.zfile.core.controller;

import im.zhaojun.zfile.core.util.StringUtils;
import im.zhaojun.zfile.module.config.model.dto.SystemConfigDTO;
import im.zhaojun.zfile.module.config.service.SystemConfigService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.nio.charset.StandardCharsets;

/**
 * 处理前端首页 Controller
 *
 * @author zhaojun
 */
@Slf4j
@Controller
public class FrontIndexController {

	@Resource
	private SystemConfigService systemConfigService;

	@Resource
	private WebProperties webProperties;

	/**
	 * 所有未找到的页面都跳转到首页, 用户解决 vue history 直接访问 404 的问题
	 * 同时, 读取 index.html 文件, 修改 title 和 favicon 后返回.
	 *
	 * @return  转发到 /index.html
	 */
	@RequestMapping(value = { "/"})
	@ResponseBody
	public ResponseEntity<String> redirect() {
		// 读取 resources/static/index.html 文件修改 title 和 favicon 后返回
		ResourceLoader resourceLoader = new FileSystemResourceLoader();
		String[] staticLocations = webProperties.getResources().getStaticLocations();

		// 如果 staticLocations 里没有包含 file:static/, 则手动添加
		boolean fileStaticExist = false;
		for (String staticLocation : staticLocations) {
			if (staticLocation.startsWith("file:")) {
				fileStaticExist = true;
				break;
			}
		}
		if (!fileStaticExist) {
			staticLocations = org.apache.commons.lang3.ArrayUtils.add(staticLocations, "file:static/");
		}

		for (String staticLocation : staticLocations) {
			org.springframework.core.io.Resource resource = resourceLoader.getResource(staticLocation + "/index.html");
			boolean exists = resource.exists();
			if (exists) {
				String content;
				try {
					content = resource.getContentAsString(StandardCharsets.UTF_8);
					if (log.isTraceEnabled()) {
						log.trace("读取 index.html 文件成功, 文件路径: {}", staticLocation);
					}
				} catch (Exception e) {
					log.error("{} 资源存在但读取 index.html 文件失败.", staticLocation);
					return ResponseEntity.status(500).body("static index.html read error");
				}

				SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();

				// 替换为系统设置中的站点名称
				String siteName = systemConfig.getSiteName();
				if (StringUtils.isNotBlank(siteName)) {
					content = content.replace("<title>ZFile</title>", "<title>" + siteName + "</title>");
				}

				// 替换为系统设置中的 favicon 地址
				String faviconUrl = systemConfig.getFaviconUrl();
				if (StringUtils.isNotBlank(faviconUrl)) {
					content = content.replace("/favicon.svg", faviconUrl);
				}

				// 添加缓存控制头
				return ResponseEntity.ok()
						.header("Cache-Control", "max-age=600, must-revalidate, proxy-revalidate")						.header("Pragma", "no-cache")
						.body(content);
			}
		}

		return ResponseEntity.status(404).body("static index.html not found");
	}

	@RequestMapping(value = { "/guest"})
	@ResponseBody
	public String guest() {
		SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
		return systemConfig.getGuestIndexHtml();
	}

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/controller/LogController.java
================================================
package im.zhaojun.zfile.core.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ZipUtil;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import im.zhaojun.zfile.core.annotation.DemoDisable;
import im.zhaojun.zfile.core.util.FileResponseUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.util.Date;

/**
 * 获取系统日志接口
 *
 * @author zhaojun
 */
@Tag(name = "日志")
@ApiSort(8)
@Slf4j
@RestController
@RequestMapping("/admin")
public class LogController {

    @Value("${zfile.log.path}")
    private String zfileLogPath;

    @GetMapping("/log/download")
    @Operation(summary = "下载系统日志")
    @DemoDisable
    public ResponseEntity<Resource> downloadLog() {
        if (log.isDebugEnabled()) {
            log.debug("下载诊断日志");
        }

        File fileZip = ZipUtil.zip(zfileLogPath);
        String currentDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        return FileResponseUtil.exportSingleThread(fileZip, "ZFile 诊断日志 - " + currentDate + ".zip");
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/ErrorCode.java
================================================
package im.zhaojun.zfile.core.exception;

import lombok.Getter;

/**
 * 异常信息枚举类
 *
 * @author zhaojun
 */
@Getter
public enum ErrorCode {

    /**
     * 系统异常
     */
    SYSTEM_ERROR("50000", "系统异常"),
    INVALID_STORAGE_SOURCE("50001", "无效或初始化失败的存储源"),
    DEMO_SITE_DISABLE_OPERATOR("50002", "演示站点不允许此操作"),

    /**
     * 业务异常 4xxxx.
     * 第二位为 0 时,是系统初始化相关错误
     * 第二位为 1 时,是前台(文件管理)错误
     * 第二位为 2 时,是登录错误
     * 第二位为 3 时,是管理员端错误
     */
    BIZ_ERROR("40000", "操作失败"),
    BIZ_NOT_FOUND("40400", "NOT FOUND"),

    // 第二位为 0 时,是系统初始化相关错误
    BIZ_SYSTEM_ALREADY_INIT("40001", "系统已初始化,请勿重复初始化"),
    BIZ_SYSTEM_INIT_ERROR("40002", "系统初始化错误"),

    // 第二位为 1 时,是前台(文件管理)错误
    BIZ_BAD_REQUEST("41000", "请求参数异常"),
    BIZ_UNSUPPORTED_PROXY_DOWNLOAD("41001", "该存储源不支持代理下载"),
    BIZ_INVALID_SIGNATURE("41002", "签名无效或下载地址已过期"),
    BIZ_PREVIEW_FILE_SIZE_EXCEED("41003", "预览文本文件大小超出系统限制"),
    BIZ_FILE_NOT_EXIST("41004", "文件不存在"),
    BIZ_ACCESS_TOO_FREQUENT("41005", "请求太频繁了,请稍后再试"),
    BIZ_UPLOAD_FILE_NOT_EMPTY("41006", "上传文件不能为空"),
    BIZ_UPLOAD_FILE_ERROR("41010", "上传文件失败"),
    BIZ_UPLOAD_FILE_TIMEOUT_ERROR("41026", "上传文件超时"),
    BIZ_EXPIRE_TIME_ILLEGAL("41007", "过期时间不合法"),
    BIZ_DELETE_FILE_NOT_EMPTY("41008", "非空文件夹不允许删除"),
    BIZ_FILE_PATH_ILLEGAL("41009", "文件名/路径存在安全隐患"),
    BIZ_DIRECT_LINK_NOT_ALLOWED("41011", "当前系统不允许使用直链"),
    BIZ_SHORT_LINK_NOT_ALLOWED("41012", "当前系统不允许使用短链"),
    BIZ_SHORT_LINK_EXPIRED("41013", "短链已失效"),
    BIZ_SHORT_LINK_NOT_FOUNT("41014", "短链不存在"),
    BIZ_DIRECT_LINK_EXPIRED("41015", "直链已失效"),
    BIZ_STORAGE_NOT_SUPPORT_OPERATION("41016", "该存储类型不支持此操作"),
    BIZ_STORAGE_NOT_FOUND("41017", "存储源不存在"),
    BIZ_STORAGE_SOURCE_ILLEGAL_OPERATION("41018", "非法或未授权的操作"),
    BIZ_STORAGE_SOURCE_FILE_FORBIDDEN("41019", "文件目录无访问权限"),
    BIZ_STORAGE_SOURCE_FOLDER_PASSWORD_REQUIRED("41020", "此文件夹需要密码"),
    BIZ_STORAGE_SOURCE_FOLDER_PASSWORD_ERROR("41021", "密码错误"),
    BIZ_INVALID_FILE_NAME("41022", "文件名不合法"),
    BIZ_UNSUPPORTED_OPERATION("41023", "不支持的操作"),
    BIZ_FTP_CLIENT_POOL_FULL("41024", "FTP 客户端连接池已满"),
    BIZ_SFTP_CLIENT_POOL_FULL("41025", "SFTP 客户端连接池已满"),
    BIZ_FOLDER_NOT_EXIST("41026", "文件夹不存在"),
    BIZ_UPLOAD_FILE_TYPE_NOT_ALLOWED("41027", "不允许上传的文件"),
    BIZ_RENAME_FILE_TYPE_NOT_ALLOWED("41028", "不允许重命名到该名称"),
    BIZ_UNSUPPORTED_OPERATION_TYPE("41029", "不支持的操作类型"),
    BIZ_CUSTOM_SHARE_LINK_KEY_FORMAT_ILLEGAL("41030", "自定义分享 key 格式不正确,只能包含字母、数字、下划线和短横线,长度为 3-8 位"),
    BIZ_SHARE_LINK_KEY_ALREADY_EXIST("41031", "分享 key 已存在"),
    BIZ_SHARE_LINK_EXPIRY_MUST_BE_FUTURE("41032", "过期时间必须是未来的时间"),
    BIZ_SHARE_LINK_NOT_EXIST("41033", "分享链接不存在"),
    BIZ_SHARE_LINK_EXPIRED("41034", "分享链接已过期"),
    BIZ_SHARE_PASSWORD_ERROR("41036", "分享密码错误"),
    BIZ_SHARE_FILE_LIST_ERROR("41037", "获取分享文件列表失败"),
    BIZ_SHARE_FILE_DOWNLOAD_ERROR("41038", "获取文件下载地址失败"),
    BIZ_SHARE_FILE_INFO_ERROR("41039", "获取文件信息失败"),

    // 第二位为 2 时,是登录错误
    BIZ_UNAUTHORIZED("42000", "未登录或未授权"),
    BIZ_LOGIN_ERROR("42001", "登录失败, 账号或密码错误"),
    BIZ_VERIFY_CODE_ERROR("42002", "验证码错误或已失效"),

    // 第二位为 3 时,是管理员端错误
    BIZ_ADMIN_ERROR("43000", "操作失败"),
    BIZ_USER_NOT_EXIST("43001", "用户不存在"),
    BIZ_USER_EXIST("43002", "用户已存在"),
    BIZ_PASSWORD_NOT_SAME("43003", "两次密码不一致"),
    BIZ_OLD_PASSWORD_ERROR("43004", "旧密码不匹配"),
    BIZ_DELETE_BUILT_IN_USER("43005", "不能删除内置用户"),
    BIZ_UNSUPPORTED_STORAGE_TYPE("43006", "不支持的存储类型"),
    BIZ_STORAGE_KEY_EXIST("43007", "存储源别名已存在"),
    BIZ_AUTO_GET_SHARE_POINT_SITES_ERROR("43008", "自动获取 SharePoint 网站列表失败"),
    BIZ_ORIGINS_NOT_EMPTY("43009", "请先在 \"站点设置\" 中配置站点域名"),
    BIZ_2FA_CODE_ERROR("43010", "双因素认证验证失败"),
    BIZ_STORAGE_INIT_ERROR("43011", "存储源初始化失败"),
    BIZ_RULE_EXIST("43012", "规则已存在"),
    BIZ_SSO_PROVIDER_EXIST("43013", "单点登录配置已存在"),
    BIZ_SSO_PROVIDER_DISABLED("43014", "此单点登录未启用"),


    /**
     * 通用的无权限异常
     */
    NO_FORBIDDEN("30000", "没有权限"),
    NO_CUSTOM_SHARE_LINK_KEY_PERMISSION("30001", "没有自定义分享链接 key 的权限"),



    /**
     * 授权校验异常
     */
    PRO_AUTH_CODE_EMPTY("20000", "请先去后台 \"基本设置\" 填写 \"授权码\""),
    PRO_CHECK_REFERER_EMPTY("20001", "Referer 无效,请检查服务端设置,20001"), // Referer 无效,请检查服务端设置
    PRO_CHECK_TIME_NO_SYNC("20002", "授权校验失败, 服务器时间异常,20002"), // 授权校验失败, 服务器时间异常.
    PRO_AUTH_CODE_INVALID_ERROR("20003", "授权码无效, 请检查后台 \"站点设置\" 中的 \"授权码\" 20003"),
    PRO_CHECK_UNKNOWN_ERROR("20004", "授权验证异常,未知异常,20098"),
    PRO_MSG_ERROR("20005", null);

    private String code;

    private String message;

    ErrorCode(String code, String message) {
        this.code = code;
        this.message = message;
    }

    /**
     * 设置错误码
     *
     * @param code 错误码
     * @return 返回当前枚举
     */
    public ErrorCode setCode(String code) {
        this.code = code;
        return this;
    }

    /**
     * 设置错误信息
     *
     * @param message 错误信息
     * @return 返回当前枚举
     */
    public ErrorCode setMessage(String message) {
        this.message = message;
        return this;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/GlobalExceptionHandler.java
================================================
package im.zhaojun.zfile.core.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotRoleException;
import im.zhaojun.zfile.core.controller.FrontIndexController;
import im.zhaojun.zfile.core.exception.biz.*;
import im.zhaojun.zfile.core.exception.core.BizException;
import im.zhaojun.zfile.core.exception.core.ErrorPageBizException;
import im.zhaojun.zfile.core.exception.core.SystemException;
import im.zhaojun.zfile.core.exception.status.*;
import im.zhaojun.zfile.core.exception.system.UploadFileFailSystemException;
import im.zhaojun.zfile.core.exception.system.ZFileAuthorizationSystemException;
import im.zhaojun.zfile.core.util.AjaxJson;
import im.zhaojun.zfile.core.util.RequestHolder;
import im.zhaojun.zfile.core.util.StringUtils;
import im.zhaojun.zfile.module.config.service.SystemConfigService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import org.sqlite.SQLiteException;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 全局异常处理
 *
 * @author zhaojun
 */
@ControllerAdvice
@Slf4j
@Order(1)
public class GlobalExceptionHandler {

    private static final ThreadLocal<String> exceptionMessage = new ThreadLocal<>();

    @Resource
    private SystemConfigService systemConfigService;

    @Resource
    private FrontIndexController frontIndexController;


    private static final int MAX_FIND_CAUSE_EXCEPTION_DEPTH = 10;

    // ---------------------- status exception start ----------------------

    @ExceptionHandler(value = UnauthorizedAccessException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public AjaxJson<?> unauthorizedAccessException() {
        if (RequestHolder.isAxiosRequest()) {
            return AjaxJson.getUnauthorizedResult();
        }
        try {
            String unauthorizedUrl = systemConfigService.getUnauthorizedUrl();
            RequestHolder.getResponse().sendRedirect(unauthorizedUrl);
        } catch (IOException ex) {
            return AjaxJson.getUnauthorizedResult();
        }

        return null;
    }

    @ExceptionHandler(value = {
            NotRoleException.class
    })
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public AjaxJson<?> forbiddenAccessException() {
        if (RequestHolder.isAxiosRequest()) {
            return AjaxJson.getForbiddenResult();
        }
        try {
            String forbiddenUrl = systemConfigService.getForbiddenUrl();
            RequestHolder.getResponse().sendRedirect(forbiddenUrl);
        } catch (IOException ex) {
            return AjaxJson.getForbiddenResult();
        }

        return null;
    }

    @ExceptionHandler(value = ForbiddenAccessException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public AjaxJson<?> forbiddenAccessException(ForbiddenAccessException e) {
        if (RequestHolder.isAxiosRequest()) {
            return AjaxJson.getError(e.getCode(), e.getMessage());
        }
        try {
            String forbiddenUrl = systemConfigService.getForbiddenUrl(e.getCode(), e.getMessage());
            RequestHolder.getResponse().sendRedirect(forbiddenUrl);
        } catch (IOException ex) {
            return AjaxJson.getError(e.getCode(), e.getMessage());
        }

        return null;
    }

    @ExceptionHandler(value = NotFoundAccessException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public AjaxJson<?> notFoundAccessException(NotFoundAccessException e) {
        if (RequestHolder.isAxiosRequest()) {
            return AjaxJson.getError(e.getCode(), e.getMessage());
        }
        try {
            String notFoundUrl = systemConfigService.getNotFoundUrl(e.getCode(), e.getMessage());
            RequestHolder.getResponse().sendRedirect(notFoundUrl);
        } catch (IOException ex) {
            return AjaxJson.getError(e.getCode(), e.getMessage());
        }

        return null;
    }


    /**
     * 所有未找到的页面都跳转到首页, 用户解决 vue history 直接访问 404 的问题
     * 同时, 读取 index.html 文件, 修改 title 和 favicon 后返回.
     *
     * @return  转发到 /index.html
     */
    @ExceptionHandler(value = NoResourceFoundException.class)
    @ResponseBody
    public String notFoundAccessException() {
        return frontIndexController.redirect().getBody();
    }

    @ExceptionHandler(value = MethodNotAllowedAccessException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public AjaxJson<String> methodNotAllowedAccessException(MethodNotAllowedAccessException e) {
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(value = BadRequestAccessException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public AjaxJson<String> badRequestAccessException(BadRequestAccessException e) {
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    // ---------------------- status exception end ----------------------




    // ---------------------- biz exception start ----------------------

    @ExceptionHandler(value = APIHttpRequestBizException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<String> apiHttpRequestBizException(APIHttpRequestBizException e) {
        log.warn("请求第三方 API 异常, 请求地址: {}, 响应码: {}, 响应体: {}", e.getUrl(), e.getResponseCode(), e.getResponseBody());
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(value = FilePathSecurityBizException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public AjaxJson<String> filePathSecurityBizException(FilePathSecurityBizException e) {
        log.warn("获取文件路径存在安全风险, 文件路径: {}", e.getPath());
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(value = GetPreviewTextContentBizException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<String> getPreviewTextContentBizException(GetPreviewTextContentBizException e) {
        log.warn("获取预览文件内容失败, 文件 url: {}", e.getUrl(), e);
        return new AjaxJson<>(e.getCode(), "预览文件内容失败, 请联系管理员.");
    }

    @ExceptionHandler(value = InitializeStorageSourceBizException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<String> initializeStorageSourceBizException(InitializeStorageSourceBizException e) {
        log.error("存储源初始化失败, 存储源 ID: {}.", e.getStorageId(), e);
        return new AjaxJson<>(e.getCode(), "存储源初始化失败:" + e.getMessage());
    }

    @ExceptionHandler(value = StorageSourceFileForbiddenAccessBizException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<String> storageSourceFileForbiddenAccessBizException(StorageSourceFileForbiddenAccessBizException e) {
        log.warn("尝试访问不被授权的文件/目录, 存储源 ID: {}: 目录: {}", e.getStorageId(), e.getPath());
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(value = StorageSourceIllegalOperationBizException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<String> storageSourceIllegalOperationBizException(StorageSourceIllegalOperationBizException e) {
        log.warn("存储源非法或未授权的操作, 存储源 ID: {}, 操作类型: {}", e.getStorageId(), e.getAction());
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(value = CorsBizException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<String> corsBizException(CorsBizException e) {
        log.warn("跨域异常:", e);
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(value = ErrorPageBizException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<?> errorPageBizException(ErrorPageBizException e) {
        if (RequestHolder.isAxiosRequest()) {
            return AjaxJson.getError(e.getCode(), e.getMessage());
        }
        try {
            String errorPageUrl = systemConfigService.getErrorPageUrl(e.getCode(), e.getMessage());
            RequestHolder.getResponse().sendRedirect(errorPageUrl);
        } catch (IOException ex) {
            return AjaxJson.getError(e.getCode(), e.getMessage());
        }

        return null;
    }


    @ExceptionHandler(value = BizException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<String> bizException(BizException e) {
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    // ---------------------- biz exception end ----------------------


    // ---------------------- system exception end ----------------------

    @ExceptionHandler(value = UploadFileFailSystemException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<String> uploadFileFailSystemException(UploadFileFailSystemException e) {
        log.warn("上传文件失败, 存储类型: {}, 上传路径: {}, 输入流可用字节数: {}, 响应码: {}, 响应体: {}",
                e.getStorageTypeEnum(), e.getUploadPath(), e.getInputStreamAvailable(), e.getResponseCode(), e.getResponseBody());
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(value = ZFileAuthorizationSystemException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<?> zfileAuthorizationSystemException(ZFileAuthorizationSystemException e) {
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(value = SystemException.class)
    @ResponseBody
    @ResponseStatus
    public AjaxJson<?> systemException(SystemException e) {
        return new AjaxJson<>(e.getCode(), e.getMessage());
    }



    // ---------------------- system exception end ----------------------



    // ---------------------- common exception end ----------------------

    @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class})
    @ResponseBody
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public AjaxJson<Map<String, String>> handleValidException(Exception e) {
        BindingResult bindingResult = null;
        if (e instanceof MethodArgumentNotValidException) {
            bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
        } else if (e instanceof BindException) {
            bindingResult = ((BindException) e).getBindingResult();
        }
        Map<String, String> errorMap = new HashMap<>(16);

        Optional.ofNullable(bindingResult)
                .map(BindingResult::getFieldErrors)
                .ifPresent(fieldErrors -> {
                    for (FieldError fieldError : fieldErrors) {
                        errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
                    }
                });
        return new AjaxJson<>(ErrorCode.BIZ_BAD_REQUEST.getCode(), ErrorCode.BIZ_BAD_REQUEST.getMessage(), errorMap);

    }

    @ExceptionHandler({FileNotFoundException.class})
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public AjaxJson<Void> fileNotFound() {
        return AjaxJson.getError("文件不存在");
    }


    /**
     * 登录异常拦截器
     */
    @ExceptionHandler(NotLoginException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ResponseBody
    public AjaxJson<?> handlerNotLoginException(NotLoginException e) {
        if (RequestHolder.isAxiosRequest()) {
            return AjaxJson.getUnauthorizedResult();
        }
        try {
            String domain = systemConfigService.getRealFrontDomain();
            if (StringUtils.isBlank(domain)) {
                domain = "";
            }
            String loginUrl = StringUtils.concat(domain, "/login");
            RequestHolder.getResponse().sendRedirect(loginUrl);
        } catch (IOException ex) {
            return AjaxJson.getUnauthorizedResult();
        }

        return null;
    }

    @ExceptionHandler
    @ResponseBody
    @ResponseStatus
    public AjaxJson<?> extraExceptionHandler(Exception e) {
        ExceptionType exceptionType = getExceptionType(e);
        if (exceptionType == ExceptionType.IGNORE_PRINT_STACK_TRACE_EXCEPTION) {
            log.warn(e.getMessage());
        } else if (exceptionType == ExceptionType.OTHER) {
            log.error(e.getMessage(), e);
        } else if (exceptionType == ExceptionType.SPECIFY_MESSAGE_EXCEPTION) {
            if (exceptionMessage.get() != null) {
                String message = exceptionMessage.get();
                log.error("发生异常: {}", message,e );
                exceptionMessage.remove();
                return AjaxJson.getError(message);
            }
        } else if (exceptionType == ExceptionType.IGNORE_EXCEPTION) {
            // 忽略异常
            return null;
        }

        if (e.getClass() == Exception.class) {
            return AjaxJson.getError("系统异常, 请联系管理员");
        } else {
            return AjaxJson.getError(e.getMessage());
        }
    }


    private static ExceptionType getExceptionType(Exception e) {
        int findCauseCount = 0;
        do {
            if (e instanceof BizException) {
                return ExceptionType.IGNORE_PRINT_STACK_TRACE_EXCEPTION;
            } else if (e instanceof ClientAbortException) {
                return ExceptionType.IGNORE_EXCEPTION;
            } else if (e instanceof SQLiteException && e.getMessage().contains("database is locked")) {
                exceptionMessage.set("数据库繁忙,请稍后再试");
                return ExceptionType.SPECIFY_MESSAGE_EXCEPTION;
            }
            e = (Exception) e.getCause();
            findCauseCount++;
        } while (e != null && findCauseCount < MAX_FIND_CAUSE_EXCEPTION_DEPTH);

        return ExceptionType.OTHER;
    }

    enum ExceptionType {
        /**
         * 忽略打印异常信息和堆栈信息
         */
        IGNORE_EXCEPTION,

        /**
         * 仅打印异常信息, 不打印堆栈信息
         */
        IGNORE_PRINT_STACK_TRACE_EXCEPTION,

        /**
         * 不打印堆栈信息,但指定异常信息
         */
        SPECIFY_MESSAGE_EXCEPTION,

        /**
         * 其他异常, 打印异常信息和堆栈信息
         */
        OTHER;
    }
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/APIHttpRequestBizException.java
================================================
package im.zhaojun.zfile.core.exception.biz;

import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;
import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import lombok.Getter;

/**
 * 请求第三方 API 时如果返回非 2xx 状态码, 则抛出此异常. 需记录请求地址, 响应状态码, 响应内容.
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#apiHttpRequestBizException(APIHttpRequestBizException)}
 *
 * @author zhaojun
 */
@Getter
public class APIHttpRequestBizException extends BizException {

    private final String url;

    private final int responseCode;

    private final String responseBody;

    public APIHttpRequestBizException(ErrorCode errorCode, String url, int responseCode, String responseBody) {
        super(errorCode);
        this.url = url;
        this.responseCode = responseCode;
        this.responseBody = responseBody;
    }

}


================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/CorsBizException.java
================================================
package im.zhaojun.zfile.core.exception.biz;

import im.zhaojun.zfile.core.exception.core.BizException;
import lombok.Getter;

/**
 * @author zhaojun
 */
@Getter
public class CorsBizException extends BizException {

    public CorsBizException(String message, Throwable cause) {
        super(message, cause);
    }

    @Override
    public boolean printExceptionStackTrace() {
        return true;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/FilePathSecurityBizException.java
================================================
package im.zhaojun.zfile.core.exception.biz;


import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;
import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import lombok.Getter;

/**
 * 文件路径安全异常, 表示文件路径不合法,如包含了 "./" 或 "../" 等字符来尝试访问非法目录.
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#filePathSecurityBizException(FilePathSecurityBizException)}
 *
 * @author zhaojun
 */
@Getter
public class FilePathSecurityBizException extends BizException {

    private final String path;

    public FilePathSecurityBizException(String path) {
        super(ErrorCode.BIZ_FILE_PATH_ILLEGAL);
        this.path = path;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/GetPreviewTextContentBizException.java
================================================
package im.zhaojun.zfile.core.exception.biz;

import im.zhaojun.zfile.core.exception.core.BizException;
import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import lombok.Getter;

/**
 * 获取预览文件内容异常, 可能是目标连接无法访问/文件不存在等原因.
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#getPreviewTextContentBizException(GetPreviewTextContentBizException)}
 *
 * @author zhaojun
 */
@Getter
public class GetPreviewTextContentBizException extends BizException {

    /**
     * 获取预览文件的 URL
     */
    private final String url;

    public GetPreviewTextContentBizException(String url, Throwable cause) {
        super(cause);
        this.url = url;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/InitializeStorageSourceBizException.java
================================================
package im.zhaojun.zfile.core.exception.biz;

import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import im.zhaojun.zfile.core.exception.core.BizException;
import lombok.Getter;

/**
 * 初始化存储源时失败产生的异常
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#initializeStorageSourceBizException(InitializeStorageSourceBizException)}
 *
 * @author zhaojun
 */
@Getter
public class InitializeStorageSourceBizException extends BizException {

    private final Integer storageId;

    public InitializeStorageSourceBizException(String message, Integer storageId) {
        super(message);
        this.storageId = storageId;
    }

    public InitializeStorageSourceBizException(String code, String message, Integer storageId, Throwable cause) {
        super(code, message, cause);
        this.storageId = storageId;
    }

}


================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/InvalidStorageSourceBizException.java
================================================
package im.zhaojun.zfile.core.exception.biz;

import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;
import lombok.Getter;

/**
 * 不存在或初始化失败的存储源异常。
 *
 * @author zhaojun
 */
@Getter
public class InvalidStorageSourceBizException extends BizException {

	private final Integer storageId;

	private final String storageKey;

	public InvalidStorageSourceBizException(String storageKey) {
		super(ErrorCode.INVALID_STORAGE_SOURCE);
		this.storageKey = storageKey;
		this.storageId = null;
	}

	public InvalidStorageSourceBizException(Integer storageId) {
		super(ErrorCode.INVALID_STORAGE_SOURCE);
		this.storageId = storageId;
		this.storageKey = null;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/StorageSourceFileForbiddenAccessBizException.java
================================================
package im.zhaojun.zfile.core.exception.biz;

import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;
import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import lombok.Getter;

/**
 * 访问了禁止访问的存储源文件/目录时抛出此异常.
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#storageSourceFileForbiddenAccessBizException(StorageSourceFileForbiddenAccessBizException)}
 *
 * @author zhaojun
 */
@Getter
public class StorageSourceFileForbiddenAccessBizException extends BizException {

    private final Integer storageId;

    private final String path;

    public StorageSourceFileForbiddenAccessBizException(Integer storageId, String path) {
        super(ErrorCode.BIZ_STORAGE_SOURCE_FILE_FORBIDDEN);
        this.storageId = storageId;
        this.path = path;
    }
}


================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/StorageSourceIllegalOperationBizException.java
================================================
package im.zhaojun.zfile.core.exception.biz;

import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;
import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import im.zhaojun.zfile.module.storage.model.enums.FileOperatorTypeEnum;
import lombok.Getter;

/**
 * 对存储源进行非法(未授权)的操作产生的异常
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#storageSourceIllegalOperationBizException(StorageSourceIllegalOperationBizException)}
 *
 * @author zhaojun
 */
@Getter
public class StorageSourceIllegalOperationBizException extends BizException {

    private final Integer storageId;

    private final FileOperatorTypeEnum action;

    public StorageSourceIllegalOperationBizException(Integer storageId, FileOperatorTypeEnum action) {
        super(ErrorCode.BIZ_STORAGE_SOURCE_ILLEGAL_OPERATION);
        this.storageId = storageId;
        this.action = action;
    }
}


================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/core/BizException.java
================================================
package im.zhaojun.zfile.core.exception.core;

import im.zhaojun.zfile.core.exception.ErrorCode;
import lombok.Getter;

/**
 * 业务异常,该类异常用户可自行处理,无需记录日志,属于正常业务流程中的异常. 如: 用户名密码错误, 未登录等.
 *
 * @author zhaojun
 */
@Getter
public class BizException extends RuntimeException {

    private static final long serialVersionUID = 8312907182931723379L;

    /**
     * 错误码
     */
    private String code;

    /**
     * 是否打印堆栈信息,业务异常默认不打印堆栈信息,如果需要打印堆栈信息,可以通过子类覆盖该方法修改返回值为 true.
     */
    public boolean printExceptionStackTrace() {
        return false;
    }

    /**
     * 构造一个没有错误信息的 <code>SystemException</code>
     */
    public BizException() {
        super();
    }

    /**
     * 使用指定的 Throwable 和 Throwable.toString() 作为异常信息来构造 SystemException
     *
     * @param cause 错误原因, 通过 Throwable.getCause() 方法可以获取传入的 cause信息
     */
    public BizException(Throwable cause) {
        super(cause);
    }

    /**
     * 使用错误信息 message 构造 SystemException
     *
     * @param message 错误信息
     */
    public BizException(String message) {
        super(message);
    }

    /**
     * 使用错误码和错误信息构造 SystemException
     *
     * @param code    错误码
     * @param message 错误信息
     */
    public BizException(String code, String message) {
        super(message);
        this.code = code;
    }

    /**
     * 使用错误信息和 Throwable 构造 SystemException
     *
     * @param message 错误信息
     * @param cause   错误原因
     */
    public BizException(String message, Throwable cause) {
        super(message, cause);
    }

    /**
     * @param code    错误码
     * @param message 错误信息
     * @param cause   错误原因
     */
    public BizException(String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    /**
     * @param errorCode ErrorCode
     */
    public BizException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
    }

    /**
     * @param errorCode ErrorCode
     * @param cause     错误原因
     */
    public BizException(ErrorCode errorCode, Throwable cause) {
        super(errorCode.getMessage(), cause);
        this.code = errorCode.getCode();
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/core/ErrorPageBizException.java
================================================
package im.zhaojun.zfile.core.exception.core;

import im.zhaojun.zfile.core.exception.ErrorCode;
import lombok.Getter;

/**
 * 业务异常,该类异常用户可自行处理,无需记录日志,属于正常业务流程中的异常. 如: 用户名密码错误, 未登录等.<br>
 * 使用该类的异常,当该异常被抛出时,会跳转到 500 错误页面(错误码和错误消息可被 {@link #code} 和 {@link #getMessage()} 覆盖),而不是返回 JSON 数据.<br>
 * 一般使用该异常得请求不会是 AJAX 请求,而是直接在浏览器中访问的页面请求.
 *
 * @author zhaojun
 */
@Getter
public class ErrorPageBizException extends RuntimeException {

    private static final long serialVersionUID = 8312907182931723379L;

    /**
     * 错误码
     */
    private String code;

    /**
     * 是否打印堆栈信息,业务异常默认不打印堆栈信息,如果需要打印堆栈信息,可以通过子类覆盖该方法修改返回值为 true.
     */
    public boolean printExceptionStackTrace() {
        return false;
    }

    /**
     * 构造一个没有错误信息的 <code>SystemException</code>
     */
    public ErrorPageBizException() {
        super();
    }

    /**
     * 使用指定的 Throwable 和 Throwable.toString() 作为异常信息来构造 SystemException
     *
     * @param cause 错误原因, 通过 Throwable.getCause() 方法可以获取传入的 cause信息
     */
    public ErrorPageBizException(Throwable cause) {
        super(cause);
    }

    /**
     * 使用错误信息 message 构造 SystemException
     *
     * @param message 错误信息
     */
    public ErrorPageBizException(String message) {
        super(message);
    }

    /**
     * 使用错误码和错误信息构造 SystemException
     *
     * @param code    错误码
     * @param message 错误信息
     */
    public ErrorPageBizException(String code, String message) {
        super(message);
        this.code = code;
    }

    /**
     * 使用错误信息和 Throwable 构造 SystemException
     *
     * @param message 错误信息
     * @param cause   错误原因
     */
    public ErrorPageBizException(String message, Throwable cause) {
        super(message, cause);
    }

    /**
     * @param code    错误码
     * @param message 错误信息
     * @param cause   错误原因
     */
    public ErrorPageBizException(String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    /**
     * @param errorCode ErrorCode
     */
    public ErrorPageBizException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
    }

    /**
     * @param errorCode ErrorCode
     * @param cause     错误原因
     */
    public ErrorPageBizException(ErrorCode errorCode, Throwable cause) {
        super(errorCode.getMessage(), cause);
        this.code = errorCode.getCode();
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/core/SystemException.java
================================================
package im.zhaojun.zfile.core.exception.core;

import im.zhaojun.zfile.core.exception.ErrorCode;
import lombok.Getter;

/**
 * 系统异常, 该类异常用户无法处理,需要记录日志, 属于系统异常. 如: 网络异常, 服务器异常等.
 *
 * @author zhaojun
 */
@Getter
public class SystemException extends RuntimeException {

    private static final long serialVersionUID = 8312907182931723379L;

    /**
     * 错误码
     */
    private String code;

    /**
     * 构造一个没有错误信息的 <code>SystemException</code>
     */
    public SystemException() {
        super();
    }

    /**
     * 使用指定的 Throwable 和 Throwable.toString() 作为异常信息来构造 SystemException
     *
     * @param cause 错误原因, 通过 Throwable.getCause() 方法可以获取传入的 cause信息
     */
    public SystemException(Throwable cause) {
        super(cause);
    }

    /**
     * 使用错误信息 message 构造 SystemException
     *
     * @param message 错误信息
     */
    public SystemException(String message) {
        super(message);
    }

    /**
     * 使用错误码和错误信息构造 SystemException
     *
     * @param code    错误码
     * @param message 错误信息
     */
    public SystemException(String code, String message) {
        super(message);
        this.code = code;
    }

    /**
     * 使用错误信息和 Throwable 构造 SystemException
     *
     * @param message 错误信息
     * @param cause   错误原因
     */
    public SystemException(String message, Throwable cause) {
        super(message, cause);
    }

    /**
     * @param code    错误码
     * @param message 错误信息
     * @param cause   错误原因
     */
    public SystemException(String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    /**
     * @param errorCode ErrorCode
     */
    public SystemException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
    }

    /**
     * @param errorCode ErrorCode
     * @param cause     错误原因
     */
    public SystemException(ErrorCode errorCode, Throwable cause) {
        super(errorCode.getMessage(), cause);
        this.code = errorCode.getCode();
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/status/BadRequestAccessException.java
================================================
package im.zhaojun.zfile.core.exception.status;

import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import im.zhaojun.zfile.core.exception.core.BizException;

/**
 * 错误请求异常, 表示请求参数有误或者服务器无法理解, 一般返回 400 状态码
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#badRequestAccessException(BadRequestAccessException)}
 *
 * @author zhaojun
 */
public class BadRequestAccessException extends BizException {

    public BadRequestAccessException(String message) {
        super(message);
    }
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/status/ForbiddenAccessException.java
================================================
package im.zhaojun.zfile.core.exception.status;

import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;

/**
 * 禁止访问异常, 表示用户没有权限访问该资源, 一般返回 403 状态码. (已经有身份,如果没有身份,应该是 UnauthorizedAccessException)
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link im.zhaojun.zfile.core.exception.GlobalExceptionHandler#forbiddenAccessException}
 *
 * @author zhaojun
 */
public class ForbiddenAccessException extends BizException {

    public ForbiddenAccessException(ErrorCode errorCode) {
        super(errorCode);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/status/MethodNotAllowedAccessException.java
================================================
package im.zhaojun.zfile.core.exception.status;

import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import im.zhaojun.zfile.core.exception.core.BizException;

/**
 * 错误请求异常, 表示请求方法有误或者服务器无法理解, 一般返回 405 状态码
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#methodNotAllowedAccessException(MethodNotAllowedAccessException)}
 *
 * @author zhaojun
 */
public class MethodNotAllowedAccessException extends BizException {

    public MethodNotAllowedAccessException(ErrorCode errorCode) {
        super(errorCode);
    }
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/status/NotFoundAccessException.java
================================================
package im.zhaojun.zfile.core.exception.status;

import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;

/**
 * 访问内容不存在异常, 表示用户请求的资源不存在时抛出, 一般返回 404 状态码.
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link im.zhaojun.zfile.core.exception.GlobalExceptionHandler#notFoundAccessException}
 *
 * @author zhaojun
 */
public class NotFoundAccessException extends BizException {

    public NotFoundAccessException(ErrorCode errorCode) {
        super(errorCode);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/status/UnauthorizedAccessException.java
================================================
package im.zhaojun.zfile.core.exception.status;

import im.zhaojun.zfile.core.exception.core.BizException;

/**
 * 禁止访问异常, 表示用户未进行身份认证, 一般返回 401 状态码.
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link im.zhaojun.zfile.core.exception.GlobalExceptionHandler#unauthorizedAccessException}
 *
 * @author zhaojun
 */
public class UnauthorizedAccessException extends BizException {

    public UnauthorizedAccessException(String message) {
        super(message);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/system/UploadFileFailSystemException.java
================================================
package im.zhaojun.zfile.core.exception.system;

import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import im.zhaojun.zfile.core.exception.core.SystemException;
import im.zhaojun.zfile.module.storage.model.enums.StorageTypeEnum;
import lombok.Getter;


/**
 * 上传文件失败系统异常, 该异常用户无法处理,需要记录日志, 属于系统异常. 如: 网络异常, 目标存储源异常等
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#uploadFileFailSystemException(UploadFileFailSystemException)}
 *
 * @author zhaojun
 */
@Getter
public class UploadFileFailSystemException extends SystemException {

    private final StorageTypeEnum storageTypeEnum;

    private final String uploadPath;

    private final Long inputStreamAvailable;

    private final int responseCode;

    private final String responseBody;

    public UploadFileFailSystemException(StorageTypeEnum storageTypeEnum, String uploadPath, Long inputStreamAvailable, int responseCode, String responseBody) {
        this(storageTypeEnum, uploadPath, inputStreamAvailable, responseCode, responseBody, null);
    }

    public UploadFileFailSystemException(StorageTypeEnum storageTypeEnum, String uploadPath, Long inputStreamAvailable, int responseCode, String responseBody, Throwable cause) {
        super(ErrorCode.BIZ_UPLOAD_FILE_ERROR, cause);
        this.storageTypeEnum = storageTypeEnum;
        this.uploadPath = uploadPath;
        this.inputStreamAvailable = inputStreamAvailable;
        this.responseCode = responseCode;
        this.responseBody = responseBody;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/exception/system/ZFileAuthorizationSystemException.java
================================================
package im.zhaojun.zfile.core.exception.system;

import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.GlobalExceptionHandler;
import im.zhaojun.zfile.core.exception.core.SystemException;

/**
 * ZFile 授权异常
 * <p/>
 * 需要全局异常处理器捕获此异常, 并记录日志. {@link GlobalExceptionHandler#zfileAuthorizationSystemException(ZFileAuthorizationSystemException)}
 *
 * @author zhaojun
 */
public class ZFileAuthorizationSystemException extends SystemException {

    public ZFileAuthorizationSystemException(String code, String message) {
        super(code, message);
    }

    public ZFileAuthorizationSystemException(ErrorCode errorCode) {
        super(errorCode);
    }

    public ZFileAuthorizationSystemException(ErrorCode errorCode, Throwable cause) {
        super(errorCode, cause);
    }

}


================================================
FILE: src/main/java/im/zhaojun/zfile/core/filter/CorsFilter.java
================================================
package im.zhaojun.zfile.core.filter;

import cn.hutool.core.util.ObjectUtil;
import im.zhaojun.zfile.core.constant.ZFileHttpHeaderConstant;
import im.zhaojun.zfile.core.util.StringUtils;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * 开启跨域支持. 一般用于开发环境, 或前后端分离部署时开启.
 *
 * @author zhaojun
 */
@WebFilter(urlPatterns = "/*")
@Order(Integer.MIN_VALUE)
@Component
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        if (httpServletRequest.getRequestURI().equals("/favicon.ico")) {
            return;
        }
    
        String header = httpServletRequest.getHeader(HttpHeaders.ORIGIN);

        List<String> allowHeaders = Arrays.asList("Origin", "X-Requested-With", "Content-Type", "Accept", ZFileHttpHeaderConstant.ZFILE_TOKEN, ZFileHttpHeaderConstant.AXIOS_REQUEST, ZFileHttpHeaderConstant.AXIOS_FROM);
        httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, ObjectUtil.defaultIfNull(header, "*"));
        httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, StringUtils.join(",", allowHeaders));
        httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE, OPTIONS");
        httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "false");
        httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "600");

        if (!CorsUtils.isPreFlightRequest(httpServletRequest)) {
            chain.doFilter(httpServletRequest, httpServletResponse);
        }
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/filter/MDCFilter.java
================================================
package im.zhaojun.zfile.core.filter;

import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import im.zhaojun.zfile.core.constant.MdcConstant;
import im.zhaojun.zfile.core.util.ZFileAuthUtil;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.MDC;

import java.io.IOException;

/**
 * MDC 过滤器, 用于写入 TraceId, 请求 IP, 用户名等信息到日志中.
 *
 * @author zhaojun
 */
@WebFilter(urlPatterns = "/*")
public class MDCFilter implements Filter {
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		HttpServletResponse httpServletResponse = (HttpServletResponse) response;
		
		MDC.put(MdcConstant.TRACE_ID, IdUtil.fastUUID());
		MDC.put(MdcConstant.IP, JakartaServletUtil.getClientIP(httpServletRequest));
		MDC.put(MdcConstant.USER, ZFileAuthUtil.getCurrentUserId().toString());
		
		try {
			filterChain.doFilter(httpServletRequest, httpServletResponse);
		} finally {
			MDC.clear();
		}
	}
	
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/filter/SecurityFilter.java
================================================
package im.zhaojun.zfile.core.filter;

import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.extra.spring.SpringUtil;
import im.zhaojun.zfile.core.constant.RuleTypeConstant;
import im.zhaojun.zfile.core.util.StringUtils;
import im.zhaojun.zfile.core.util.matcher.IRuleMatcher;
import im.zhaojun.zfile.core.util.matcher.RuleMatcherFactory;
import im.zhaojun.zfile.module.config.model.dto.SystemConfigDTO;
import im.zhaojun.zfile.module.config.service.SystemConfigService;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;

import java.io.IOException;
import java.util.List;

/**
 * 检测访问的 IP 和 UA 是否符合系统安全设置中的规则
 *
 * @author zhaojun
 */
@WebFilter(urlPatterns = "/*")
public class SecurityFilter implements Filter {

    private static volatile SystemConfigService systemConfigService;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        // 双重检测锁, 防止多次初始化
        if (systemConfigService == null) {
            synchronized (this) {
                if (systemConfigService == null) {
                    systemConfigService = SpringUtil.getBean(SystemConfigService.class);
                }
            }
        }

        SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
        String accessIpBlocklist = systemConfig.getAccessIpBlocklist();
        String accessUaBlocklist = systemConfig.getAccessUaBlocklist();

        // 判断当前访问 IP 是否在黑名单中
        String currentAccessIp = JakartaServletUtil.getClientIP(httpServletRequest);
        if (StringUtils.isNotBlank(accessIpBlocklist) && checkIsDisableIP(accessIpBlocklist, currentAccessIp)) {
            httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
            httpServletResponse.getWriter().write("disable access.[" + currentAccessIp + "]");
            return;
        }

        // 判断当前访问 User-Agent 是否在黑名单中
        String userAgent = httpServletRequest.getHeader(HttpHeaders.USER_AGENT);
        if (StringUtils.isNotBlank(accessUaBlocklist) && checkIsDisableUA(accessUaBlocklist, userAgent)) {
            httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
            httpServletResponse.getWriter().write("disable access.[" + userAgent + "]");
            return;
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    private boolean checkIsDisableIP(String accessIpBlocklist, String currentAccessIp) {
        IRuleMatcher ruleMatcher = RuleMatcherFactory.getRuleMatcher(RuleTypeConstant.IP);
        List<String> ruleList = StringUtils.split(accessIpBlocklist, StringUtils.LF);
        return ruleMatcher.matchAny(ruleList, currentAccessIp);
    }

    private boolean checkIsDisableUA(String accessUaBlocklist, String currentAccessUA) {
        IRuleMatcher ruleMatcher = RuleMatcherFactory.getRuleMatcher(RuleTypeConstant.SPRING_SIMPLE);
        List<String> ruleList = StringUtils.split(accessUaBlocklist, StringUtils.LF);
        return ruleMatcher.matchAny(ruleList, currentAccessUA);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/io/EnsureContentLengthInputStreamResource.java
================================================
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package im.zhaojun.zfile.core.io;

import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamResource;

import java.io.InputStream;

/**
 * 
 * 自定义 EnsureContentLengthInputStreamResource 可以保证必须实现 InputStream 的 contentLength 方法返回实际的长度.
 * 此类相较于 {@link org.springframework.core.io.InputStreamResource} 仅实现了 contentLength 方法.
 * <br><br>
 * {@link org.springframework.core.io.Resource} implementation for a given {@link InputStream}.
 * <p>Should only be used if no other specific {@code Resource} implementation
 * is applicable. In particular, prefer {@link ByteArrayResource} or any of the
 * file-based {@code Resource} implementations where possible.
 *
 * <p>In contrast to other {@code Resource} implementations, this is a descriptor
 * for an <i>already opened</i> resource - therefore returning {@code true} from
 * {@link #isOpen()}. Do not use an {@code InputStreamResource} if you need to
 * keep the resource descriptor somewhere, or if you need to read from a stream
 * multiple times.
 *
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 28.12.2003
 * @see ByteArrayResource
 * @see org.springframework.core.io.ClassPathResource
 * @see org.springframework.core.io.FileSystemResource
 * @see org.springframework.core.io.UrlResource
 */
public class EnsureContentLengthInputStreamResource extends InputStreamResource {

	private final long contentLength;

	/**
	 * Create a new InputStreamResource.
	 * @param inputStream the InputStream to use
	 */
	public EnsureContentLengthInputStreamResource(InputStream inputStream, long contentLength) {
        super(inputStream);
        this.contentLength = contentLength;
	}

	@Override
	public long contentLength() {
		return contentLength;
	}

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/model/request/PageQueryRequest.java
================================================
package im.zhaojun.zfile.core.model.request;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.util.Objects;

/**
 * 通用分页请求对象,可继承该类增加业务字段.
 *
 * @author zhaojun
 */
@Data
public class PageQueryRequest {
	
	@Schema(title="分页页数")
	private Integer page = 1;
	
	@Schema(title="每页条数")
	private Integer limit = 10;
	
	@Schema(title="排序字段")
	private String orderBy = "create_date";
	
	@Schema(title="排序顺序")
	private String orderDirection = "desc";

    public OrderItem getOrderItem() {
        boolean asc = Objects.equals(orderDirection, "asc");
        return asc ? OrderItem.asc(orderBy) : OrderItem.desc(orderBy);
    }
	
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/AjaxJson.java
================================================
package im.zhaojun.zfile.core.util;

import im.zhaojun.zfile.core.exception.ErrorCode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;

import java.io.Serializable;

/**
 * ajax 请求返回 JSON 格式数据的封装
 *
 * @author zhaojun
 */
@Data
@ToString
public class AjaxJson<T> implements Serializable {

	private static final long serialVersionUID = 1L;    // 序列化版本号

	public static final String CODE_SUCCESS = "0";            // 成功状态码

	@Schema(title = "业务状态码,0 为正常,其他值均为异常,异常情况下见响应消息", example = "0")
	private final String code;

	@Schema(title = "响应消息", example = "ok")
	private String msg;

	@Schema(title = "响应数据")
	private T data;

	@Schema(title = "数据总条数,分页情况有效")
	private final Long dataCount;
	
	@Schema(title = "跟踪 ID")
	private String traceId;

	public AjaxJson(String code, String msg) {
		if (code == null) {
			code = ErrorCode.SYSTEM_ERROR.getCode();
		}
		this.code = code;
		this.msg = msg;
		this.dataCount = null;
	}

	public AjaxJson(String code, String msg, T data) {
		this.code = code;
		this.msg = msg;
		this.data = data;
		this.dataCount = null;
	}

	public AjaxJson(String code, String msg, T data, Long dataCount) {
		this.code = code;
		this.msg = msg;
		this.data = data;
		this.dataCount = dataCount;
	}

	// 返回成功
	public static AjaxJson<Void> getSuccess() {
		return new AjaxJson<>(CODE_SUCCESS, "ok");
	}

	public static AjaxJson<Void> getSuccess(String msg) {
		return new AjaxJson<>(CODE_SUCCESS, msg);
	}

	public static <T> AjaxJson<T> getSuccess(String msg, T data) {
		return new AjaxJson<>(CODE_SUCCESS, msg, data);
	}

	public static <T> AjaxJson<T> getSuccessData(T data) {
		return new AjaxJson<>(CODE_SUCCESS, "ok", data);
	}

	// 返回分页和数据的
	public static <T> AjaxJson<T> getPageData(Long dataCount, T data) {
		return new AjaxJson<>(CODE_SUCCESS, "ok", data, dataCount);
	}

	// 返回错误
	public static AjaxJson<Void> getError(String msg) {
		return new AjaxJson<>(ErrorCode.SYSTEM_ERROR.getCode(), msg);
	}

	// 返回未登录
	public static AjaxJson<?> getUnauthorizedResult() {
		return new AjaxJson<>(ErrorCode.BIZ_UNAUTHORIZED.getCode(), "未登录,请登录后再次访问");
	}

	// 返回没权限的
	public static AjaxJson<?> getForbiddenResult() {
		return new AjaxJson<>(ErrorCode.NO_FORBIDDEN.getCode(), "未授权,请登录正确权限账号再试");
	}

	// 返回未找到的
	public static AjaxJson<?> getNotFoundResult() {
		return new AjaxJson<>(ErrorCode.BIZ_NOT_FOUND.getCode(), ErrorCode.BIZ_NOT_FOUND.getMessage());
	}

	public static AjaxJson<?> getError(String code, String msg) {
		return new AjaxJson<>(code, msg);
	}

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/ArrayUtils.java
================================================
package im.zhaojun.zfile.core.util;

/**
 * 数组工具类
 *
 * @author zhaojun
 */
public class ArrayUtils {

    /**
     * 数组是否为空
     *
     * @param   <T>
     *          数组元素类型
     *
     * @param   array
     *          数组
     *
     * @return  是否为空
     */
    public static <T> boolean isEmpty(T[] array) {
        return array == null || array.length == 0;
    }

    /**
     * 数组是否不为空
     *
     * @param   <T>
     *          数组元素类型
     *
     * @param   array
     *          数组
     *
     * @return  是否不为空
     */
    public static <T> boolean isNotEmpty(T[] array) {
        return !isEmpty(array);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/CharPool.java
================================================
package im.zhaojun.zfile.core.util;

public interface CharPool {

    /**
     * CHAR 常量:斜杠 {@code '/'} ASCII 47
     */
    char SLASH_CHAR = '/';

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/CharSequenceUtil.java
================================================
package im.zhaojun.zfile.core.util;

import cn.hutool.core.text.StrSplitter;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.List;

/**
 * 字符串工具类
 *
 * @author zhaojun
 */
public class CharSequenceUtil implements CharPool {

    /**
     * 找不到索引时的返回值
     */
    public static final int INDEX_NOT_FOUND = -1;

    /**
     * 字符串常量:{@code "null"} <br>
     * 注意:{@code "null" != null}
     */
    public static final String NULL = "null";

    /**
     * 字符串常量:空字符串 {@code ""}
     */
    public static final String EMPTY = "";

    /**
     * 字符串常量:空格符 {@code " "}
     */
    public static final String SPACE = " ";


    /**
     * 获取 CharSequence 的长度, 如果为 null, 返回 0
     *
     * @param   ch
     *          要获取长度的 CharSequence, 可能为 null
     *
     * @return  CharSequence 的长度
     */
    public static int length(final @Nullable CharSequence ch) {
        return ch == null ? 0 : ch.length();
    }


    /**
     * {@link CharSequence} 转为字符串
     *
     * @param   cs
     *          {@link CharSequence}
     *
     * @return  字符串
     */
    public static String str(final @Nullable CharSequence cs) {
        return null == cs ? null : cs.toString();
    }


    /**
     * 判断 CharSequence 是否为空
     *
     * @param   cs
     *          {@link CharSequence}
     *
     * @return  是否为空
     */
    public static boolean isEmpty(final @Nullable CharSequence cs) {
        return cs == null || cs.isEmpty();
    }


    /**
     * CharSequence 是否不为空
     *
     * @param   cs
     *          {@link CharSequence}
     *
     * @return  是否不为空
     */
    public static boolean isNotEmpty(final @Nullable CharSequence cs) {
        return !isEmpty(cs);
    }


    /**
     * <p>指定字符串数组中的元素,是否全部为空字符串。</p>
     * <p>如果指定的字符串数组的长度为 0,或者所有元素都是空字符串,则返回 true。</p>
     * <br>
     *
     * <p>例:</p>
     * <ul>
     *     <li>{@code CharSequenceUtil.isAllEmpty()                  // true}</li>
     *     <li>{@code CharSequenceUtil.isAllEmpty("", null)          // true}</li>
     *     <li>{@code CharSequenceUtil.isAllEmpty("123", "")         // false}</li>
     *     <li>{@code CharSequenceUtil.isAllEmpty("123", "abc")      // false}</li>
     *     <li>{@code CharSequenceUtil.isAllEmpty(" ", "\t", "\n")   // false}</li>
     * </ul>
     *
     * @param   strs
     *          字符串列表
     *
     * @return  所有字符串是否都为空
     */
    public static boolean isAllEmpty(final @Nullable CharSequence... strs) {
        if (strs == null) {
            return true;
        }

        for (CharSequence str : strs) {
            if (isNotEmpty(str)) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>是否包含空字符串。</p>
     * <p>如果指定的字符串数组的长度为 0,或者其中的任意一个元素是空字符串,则返回 true。</p>
     * <br>
     *
     * <p>例:</p>
     * <ul>
     *     <li>{@code CharSequenceUtil.hasEmpty()                  // true}</li>
     *     <li>{@code CharSequenceUtil.hasEmpty("", null)          // true}</li>
     *     <li>{@code CharSequenceUtil.hasEmpty("123", "")         // true}</li>
     *     <li>{@code CharSequenceUtil.hasEmpty("123", "abc")      // false}</li>
     *     <li>{@code CharSequenceUtil.hasEmpty(" ", "\t", "\n")   // false}</li>
     * </ul>
     *
     * @param   strs
     *          字符串列表
     *
     * @return  是否包含空字符串
     */
    public static boolean hasEmpty(final @Nullable CharSequence... strs) {
        if (ArrayUtils.isEmpty(strs)) {
            return true;
        }

        for (CharSequence str : strs) {
            if (isEmpty(str)) {
                return true;
            }
        }
        return false;
    }


    /**
     * <p>指定字符串数组中的元素,是否都不为空字符串。</p>
     * <p>如果指定的字符串数组的长度不为 0,或者所有元素都不是空字符串,则返回 true。</p>
     * <br>
     *
     * <p>例:</p>
     * <ul>
     *     <li>{@code CharSequenceUtil.isAllNotEmpty()                  // false}</li>
     *     <li>{@code CharSequenceUtil.isAllNotEmpty("", null)          // false}</li>
     *     <li>{@code CharSequenceUtil.isAllNotEmpty("123", "")         // false}</li>
     *     <li>{@code CharSequenceUtil.isAllNotEmpty("123", "abc")      // true}</li>
     *     <li>{@code CharSequenceUtil.isAllNotEmpty(" ", "\t", "\n")   // true}</li>
     * </ul>
     *
     * @param   args
     *          字符串数组
     *
     * @return  所有字符串是否都不为为空白
     */
    public static boolean isAllNotEmpty(final @Nullable CharSequence... args) {
        return !hasEmpty(args);
    }


    /**
     * 字符串是否为空白
     *
     * @param   ch
     *          要判断的字符串, 可能为 null
     *
     * @return  是否为空白
     */
    public static boolean isBlank(final @Nullable CharSequence ch) {
        final int strLen = ch == null ? 0 : ch.length();
        if (strLen == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(ch.charAt(i))) {
                return false;
            }
        }
        return true;
    }


    /**
     * 字符串是否不为空白
     *
     * @param   cs
     *          字符串
     *
     * @return  是否不为空白
     */
    public static boolean isNotBlank(final @Nullable CharSequence cs) {
        return !isBlank(cs);
    }


    /**
     * 比较两个 CharSequence 是否相等, 区分大小写, 如果两个都为 null, 返回 true
     *
     * @param   cs1
     *          CharSequence 1, 可能为 null
     *
     * @param   cs2
     *          CharSequence 2, 可能为 null
     *
     * @return  是否相等
     */
    public static boolean equals(final @Nullable CharSequence cs1, final @Nullable CharSequence cs2) {
        if (cs1 == cs2) {
            return true;
        }
        if (cs1 == null || cs2 == null) {
            return false;
        }
        if (cs1.length() != cs2.length()) {
            return false;
        }
        if (cs1 instanceof String && cs2 instanceof String) {
            return cs1.equals(cs2);
        }
        // 逐个比较
        final int length = cs1.length();
        for (int i = 0; i < length; i++) {
            if (cs1.charAt(i) != cs2.charAt(i)) {
                return false;
            }
        }
        return true;
    }


    /**
     * 比较两个 CharSequence 是否相等, 可以选择是否忽略大小写, 如果两个都为 null, 返回 true
     *
     * @param   cs1
     *          字符串 1
     *
     * @param   cs2
     *          字符串 2
     *
     * @param   ignoreCase
     *          是否忽略大小写
     *
     * @return  是否相等
     */
    public static boolean equals(final @Nullable CharSequence cs1,final @Nullable CharSequence cs2, boolean ignoreCase) {
        return ignoreCase ? equalsIgnoreCase(cs1, cs2) : equals(cs1, cs2);
    }


    /**
     * 字符串是否相等, 忽略大小写
     *
     * @param   cs1
     *          字符串 1
     *
     * @param   cs2
     *          字符串 2
     *
     * @return  忽略大小写后是否相等
     */
    public static boolean equalsIgnoreCase(final @Nullable CharSequence cs1, final @Nullable CharSequence cs2) {
        if (cs1 == cs2) {
            return true;
        }
        if (cs1 == null || cs2 == null) {
            return false;
        }
        if (cs1.length() != cs2.length()) {
            return false;
        }

        return cs1.toString().equalsIgnoreCase(cs2.toString());
    }


    /**
     * 切分字符串,如果分隔符不存在则返回原字符串
     *
     * @param   str
     *          被切分的字符串
     *
     * @param   separator
     *          分隔符
     *
     * @return  字符串
     */
    public static List<String> split(final CharSequence str, final CharSequence separator) {
        return split(str, separator, false, false);
    }


    /**
     * 切分字符串
     *
     * @param   str
     *          被切分的字符串
     *
     * @param   separator
     *          分隔符字符
     *
     * @param   isTrim
     *          是否去除切分字符串后每个元素两边的空格
     *
     * @param   ignoreEmpty
     *          是否忽略空串
     *
     * @return  切分后的集合
     */
    public static List<String> split(CharSequence str, CharSequence separator, boolean isTrim, boolean ignoreEmpty) {
        return split(str, separator, 0, isTrim, ignoreEmpty);
    }


    /**
     * 切分字符串
     *
     * @param   str
     *          被切分的字符串
     *
     * @param   separator
     *          分隔符字符
     *
     * @param   limit
     *          限制分片数,-1 不限制
     *
     * @param   isTrim
     *          是否去除切分字符串后每个元素两边的空格
     *
     * @param   ignoreEmpty
     *          是否忽略空串
     *
     * @return  切分后的集合
     */
    public static List<String> split(CharSequence str, CharSequence separator, int limit, boolean isTrim, boolean ignoreEmpty) {
        final String separatorStr = (null == separator) ? null : separator.toString();
        return StrSplitter.split(str, separatorStr, limit, isTrim, ignoreEmpty);
    }


    /**
     * 指定字符串是否在字符串中出现过
     *
     * @param   str
     *          字符串
     *
     * @param   searchStr
     *          被查找的字符串
     *
     * @return  是否包含
     */
    public static boolean contains(final @Nullable CharSequence str, final @Nullable CharSequence searchStr) {
        if (null == str || null == searchStr) {
            return false;
        }
        return str.toString().contains(searchStr);
    }


    /**
     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串
     *
     * @param   str
     *          指定字符串
     *
     * @param   testStrs
     *          需要检查的字符串数组
     *
     * @return  是否包含任意一个字符串
     */
    public static boolean containsAny(final @Nullable CharSequence str, final @Nullable  CharSequence... testStrs) {
        if (isEmpty(str) || ArrayUtils.isEmpty(testStrs)) {
            return false;
        }
        for (CharSequence checkStr : testStrs) {
            if (null != checkStr && str.toString().contains(checkStr)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串<br>
     * 忽略大小写
     *
     * @param   str
     *          指定字符串
     *
     * @param   testStrs
     *          需要检查的字符串数组
     *
     * @return  是否包含任意一个字符串
     */
    public static boolean containsAnyIgnoreCase(final @Nullable CharSequence str, final @Nullable CharSequence... testStrs) {
        return StringUtils.containsAnyIgnoreCase(str, testStrs);
    }


    /**
     * 以 conjunction 为分隔符将多个对象转换为字符串
     *
     * @param   conjunction
     *          分隔符
     *
     * @param   objs
     *          数组
     *
     * @return  连接后的字符串
     */
    public static String join(CharSequence conjunction, Object... objs) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < objs.length; i++) {
            Object item = objs[i];
            sb.append(item);
            if (i < objs.length - 1) {
                sb.append(conjunction);
            }
        }
        return sb.toString();
    }


    /**
     * 以 conjunction 为分隔符将 Collection 对象转换为字符串
     *
     * @param   conjunction
     *          分隔符
     *
     * @param   collection
     *          集合
     *
     * @return  连接后的字符串
     */
    public static String join(CharSequence conjunction, Collection<?> collection) {
        StringBuilder sb = new StringBuilder();
        for (Object item : collection) {
            sb.append(item).append(conjunction);
        }
        if (!sb.isEmpty()) {
            sb.delete(sb.length() - conjunction.length(), sb.length());
        }
        return sb.toString();
    }


    /**
     * 是否以指定字符串开头
     *
     * @param   str
     *          被监测字符串
     *
     * @param   prefix
     *          开头字符串
     *
     * @return  是否以指定字符串开头
     */
    public static boolean startWith(CharSequence str, CharSequence prefix) {
        return startWith(str, prefix, false);
    }


    /**
     * 是否以指定字符串开头,忽略大小写
     *
     * @param   str
     *          被监测字符串
     *
     * @param   prefix
     *          开头字符串
     *
     * @return  是否以指定字符串开头
     */
    public static boolean startWithIgnoreCase(CharSequence str, CharSequence prefix) {
        return startWith(str, prefix, true);
    }


    /**
     * 是否以指定字符串开头<br>
     * 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
     *
     * @param   str
     *          被监测字符串
     *
     * @param   prefix
     *          开头字符串
     *
     * @param   ignoreCase
     *          是否忽略大小写
     *
     * @return  是否以指定字符串开头
     */
    public static boolean startWith(CharSequence str, CharSequence prefix, boolean ignoreCase) {
        return startWith(str, prefix, ignoreCase, false);
    }


    /**
     * 是否以指定字符串开头<br>
     * 如果给定的字符串和开头字符串都为 null 则返回 true,否则任意一个值为 null 返回 false<br>
     * <pre>
     *     CharSequenceUtil.startWith("123", "123", false, true);   -- false
     *     CharSequenceUtil.startWith("ABCDEF", "abc", true, true); -- true
     *     CharSequenceUtil.startWith("abc", "abc", true, true);    -- false
     * </pre>
     *
     * @param   str
     *          被监测字符串
     *
     * @param   prefix
     *          开头字符串
     *
     * @param   ignoreCase
     *          是否忽略大小写
     *
     * @param   ignoreEquals
     *          是否忽略字符串相等的情况
     *
     * @return  是否以指定字符串开头
     */
    public static boolean startWith(final @Nullable CharSequence str, final @Nullable CharSequence prefix, boolean ignoreCase, boolean ignoreEquals) {
        if (null == str || null == prefix) {
            if (ignoreEquals) {
                return false;
            }
            return null == str && null == prefix;
        }

        boolean isStartWith = str.toString()
                .regionMatches(ignoreCase, 0, prefix.toString(), 0, prefix.length());

        if (isStartWith) {
            return (!ignoreEquals) || (!equals(str, prefix, ignoreCase));
        }
        return false;
    }


    /**
     * 是否以指定字符串结尾
     *
     * @param   str
     *          被监测字符串
     *
     * @param   suffix
     *          结尾字符串
     *
     * @return  是否以指定字符串结尾
     */
    public static boolean endWith(final @Nullable CharSequence str, final @Nullable CharSequence suffix) {
        return endWith(str, suffix, false);
    }


    /**
     * 是否以指定字符串结尾<br>
     * 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
     *
     * @param   str
     *          被监测字符串
     *
     * @param   suffix
     *          结尾字符串
     *
     * @param   ignoreCase
     *          是否忽略大小写
     *
     * @return  是否以指定字符串结尾
     */
    public static boolean endWith(final @Nullable CharSequence str, final @Nullable CharSequence suffix, boolean ignoreCase) {
        return endWith(str, suffix, ignoreCase, false);
    }


    /**
     * 是否以指定字符串结尾<br>
     * 如果给定的字符串和开头字符串都为null则返回true,否则任意一个值为null返回false
     *
     * @param   str
     *          被监测字符串
     *
     * @param   suffix
     *          结尾字符串
     *
     * @param   ignoreCase
     *          是否忽略大小写
     *
     * @param   ignoreEquals
     *          是否忽略字符串相等的情况
     *
     * @return  是否以指定字符串结尾
     */
    public static boolean endWith(final @Nullable CharSequence str, final @Nullable CharSequence suffix, boolean ignoreCase, boolean ignoreEquals) {
        if (null == str || null == suffix) {
            if (ignoreEquals) {
                return false;
            }
            return null == str && null == suffix;
        }

        final int strOffset = str.length() - suffix.length();
        boolean isEndWith = str.toString()
                .regionMatches(ignoreCase, strOffset, suffix.toString(), 0, suffix.length());

        if (isEndWith) {
            return (!ignoreEquals) || (!equals(str, suffix, ignoreCase));
        }
        return false;
    }


    /**
     * 去掉指定前缀
     *
     * @param   str
     *          字符串
     *
     * @param   prefix
     *          前缀
     *
     * @return  切掉后的字符串,若前缀不是 preffix, 返回原字符串
     */
    public static String removePrefix(final @Nullable CharSequence str, final @Nullable CharSequence prefix) {
        if (isEmpty(str) || isEmpty(prefix)) {
            return str(str);
        }
        String str2 = str.toString();
        String prefix2 = prefix.toString();
        if (str2.startsWith(prefix2)) {
            return str.subSequence(prefix.length(), str.length()).toString();
        }
        return str2; // 若前缀不是 prefix,返回原字符串
    }


    /**
     * 返回第一个非 {@code null} 元素
     *
     * @param   strs
     *          多个元素
     *
     * @param   <T>
     *          元素类型
     *
     * @return  第一个非空元素,如果给定的数组为空或者都为空,返回{@code null}
     */
    @SuppressWarnings("unchecked")
    public static <T extends CharSequence> T firstNonNull(T... strs) {
        if (ArrayUtils.isNotEmpty(strs)) {
            for (T str : strs) {
                if (isNotEmpty(str)) {
                    return str;
                }
            }
        }
        return null;
    }


    /**
     * 截取分隔字符串之前的字符串,不包括分隔字符串<br>
     * 如果给定的字符串为空串(null或"")或者分隔字符串为null,返回原字符串<br>
     * 如果分隔字符串为空串"",则返回空串,如果分隔字符串未找到,返回原字符串,举例如下:
     *
     * <pre>
     * CharSequenceUtil.subBefore(null, *, false)      = null
     * CharSequenceUtil.subBefore("", *, false)        = ""
     * CharSequenceUtil.subBefore("abc", "a", false)   = ""
     * CharSequenceUtil.subBefore("abcba", "b", false) = "a"
     * CharSequenceUtil.subBefore("abc", "c", false)   = "ab"
     * CharSequenceUtil.subBefore("abc", "d", false)   = "abc"
     * CharSequenceUtil.subBefore("abc", "", false)    = ""
     * CharSequenceUtil.subBefore("abc", null, false)  = "abc"
     * </pre>
     *
     * @param   string
     *          被查找的字符串
     *
     * @param   separator
     *          分隔字符串(不包括)
     *
     * @param   isLastSeparator
     *          是否查找最后一个分隔字符串(多次出现分隔字符串时选取最后一个),true为选取最后一个
     *
     * @return  切割后的字符串
     */
    public static String subBefore(final @Nullable CharSequence string, final @Nullable CharSequence separator, boolean isLastSeparator) {
        if (isEmpty(string) || separator == null) {
            return null == string ? null : string.toString();
        }

        final String str = string.toString();
        final String sep = separator.toString();
        if (sep.isEmpty()) {
            return EMPTY;
        }
        final int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
        if (INDEX_NOT_FOUND == pos) {
            return str;
        }
        if (0 == pos) {
            return EMPTY;
        }
        return str.substring(0, pos);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/ClassUtils.java
================================================
package im.zhaojun.zfile.core.util;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Class & 反射相关工具类
 *
 * @author zhaojun
 */
public class ClassUtils {

	public static Class<?> forName(String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

	/**
	 * 获取指定类的泛型类型, 只获取第一个泛型类型
	 *
	 * @param   clazz
	 *          泛型类
	 *
	 * @return  泛型类型
	 */
	public static Class<?> getClassFirstGenericsParam(Class<?> clazz) {
		Type genericSuperclass = clazz.getGenericSuperclass();
		Type actualTypeArgument = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
		return (Class<?>) actualTypeArgument;
	}

	public static Class<?> getGenericType(Field field) {
		ParameterizedType listType = (ParameterizedType) field.getGenericType();
		return (Class<?>) listType.getActualTypeArguments()[0];
	}

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/CollectionUtils.java
================================================
package im.zhaojun.zfile.core.util;

import cn.hutool.core.lang.func.Func1;

import javax.annotation.Nullable;
import java.util.*;

public class CollectionUtils {


    /**
     * 判断集合是否为空
     *
     * @param   collection
     *          集合
     *
     * @return  是否为空
     */
    public static boolean isEmpty(@Nullable Collection<?> collection) {
        return (collection == null || collection.isEmpty());
    }


    /**
     * 判断集合是否不为空
     *
     * @param   collection
     *          集合
     *
     * @return  是否不为空
     */
    public static boolean isNotEmpty(@Nullable Collection<?> collection) {
        return !isEmpty(collection);
    }


    /**
     * 从集合中获取第一个元素, 如果集合为空则返回 {@code null}
     *
     * @param   list
     *          集合,可能为 {@code null}
     *
     * @return  第一个元素,如果集合为空则返回 {@code null}
     */
    @Nullable
    public static <T> T getFirst(@Nullable List<T> list) {
        if (isEmpty(list)) {
            return null;
        }
        return list.get(0);
    }


    /**
     * 从集合中获取最后一个元素, 如果集合为空则返回 {@code null}
     *
     * @param   list
     *          集合,可能为 {@code null}
     *
     * @return  最后一个元素,如果集合为空则返回 {@code null}
     */
    @Nullable
    public static <T> T getLast(@Nullable List<T> list) {
        if (isEmpty(list)) {
            return null;
        }
        return list.get(list.size() - 1);
    }


    /**
     * 加入全部
     *
     * @param   <T>
     *          集合元素类型
     *
     * @param   collection
     *          被加入的集合 {@link Collection}
     *
     * @param   values
     *          要加入的内容数组
     *
     * @return  原集合
     */
    public static <T> Collection<T> addAll(Collection<T> collection, T[] values) {
        if (null != collection && null != values) {
            Collections.addAll(collection, values);
        }
        return collection;
    }


    /**
     * Iterable 转换为 Map, 根据指定的 keyFunc 函数生成 Key. Value 为 Iterable 中的元素.<br>
     * 可以指定将结果放入的 Map, 如不指定则会新建一个 HashMap 返回.
     *
     * @param   <K>
     *          Map Key 类型
     *
     * @param   <V>
     *          Map Value 类型
     *
     * @param   values
     *          被转换的 Iterable
     *
     * @param   map
     *          转换后的 Value 存放的 Map, 如果为 {@code null} 则新建一个 HashMap
     *
     * @param   keyFunc
     *          生成 Map 的 Key 的函数
     *
     * @return  转换后的 Map
     */
    public static <K, V> Map<K, V> toMap(final @Nullable Iterable<V> values, final @Nullable Map<K, V> map, final @Nullable Func1<V, K> keyFunc) {
        if (values == null || keyFunc == null) {
            return Collections.emptyMap();
        }

        final Map<K, V> result = map == null ? new HashMap<>() : map;

        for (V value : values) {
            try {
                result.put(keyFunc.call(value), value);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return result;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/DnsUtil.java
================================================
package im.zhaojun.zfile.core.util;

import com.alibaba.dcm.DnsCacheManipulator;
import com.alibaba.fastjson2.JSONArray;
import org.springframework.lang.Nullable;

public class DnsUtil {

    /**
     * 通过 HTTP DNS 获取域名对应的 IP 地址
     *
     * @param   domain
     *          域名
     *
     * @return  IP 地址数组
     */
    public static @Nullable String[] getDomainIpByHttpDns(String domain) {
        String jsonArrayStr = cn.hutool.http.HttpUtil.get("http://223.5.5.5/resolve?name=" + domain + "&short=1", 3000);
        JSONArray jsonArray = JSONArray.parseArray(jsonArrayStr);
        if (!jsonArray.isEmpty()) {
            String[] result = new String[jsonArray.size()];
            for (int i = 0; i < jsonArray.size(); i++) {
                result[i] = jsonArray.getString(i);
            }
            return result;
        } else {
            return null;
        }
    }


    /**
     * 通过 HTTP DNS 获取域名对应的 IP 地址, 并设置 DNS 缓存.
     *
     * @param   domain
     *          域名
     *
     * @param   cacheTime
     *          缓存时间, 单位: 毫秒
     *
     * @return  IP 地址数组
     */
    public static String[] getDomainIpByHttpDnsAndCache(String domain, int cacheTime) {
        String[] domainIpByHttpDns = getDomainIpByHttpDns(domain);
        if (domainIpByHttpDns != null) {
            // 设置 DNS 缓存
            DnsCacheManipulator.setDnsCache(cacheTime, domain, domainIpByHttpDns);
        }
        return domainIpByHttpDns;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/EnumConvertUtils.java
================================================
package im.zhaojun.zfile.core.util;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;

import java.lang.reflect.Field;

/**
 * 枚举转换工具类
 *
 * @author zhaojun
 */
public class EnumConvertUtils {


	/**
	 * 根据枚举 class 和值获取对应的枚举对象
	 *
	 * @param   clazz
	 *          枚举类 Class
	 *
	 * @param   value
	 *          枚举值
	 *
	 * @return  枚举对象
	 */
	public static Enum<?> convertStrToEnum(Class<?> clazz, Object value) {
		if (!ClassUtil.isEnum(clazz)) {
			return null;
		}

		Field[] fields = ReflectUtil.getFields(clazz);
		for (Field field : fields) {
			boolean jsonValuePresent = field.isAnnotationPresent(JsonValue.class);
			boolean enumValuePresent = field.isAnnotationPresent(EnumValue.class);

			if (jsonValuePresent || enumValuePresent) {
				Object[] enumConstants = clazz.getEnumConstants();

				for (Object enumObj : enumConstants) {
					if (ObjectUtil.equal(value, ReflectUtil.getFieldValue(enumObj, field))) {
						return (Enum<?>) enumObj;
					}
				}
			}
		}
		return null;
	}


	/**
	 * 转换枚举对象为字符串, 如果枚举对象没有定义 JsonValue 注解, 则使用 EnumValue 注解的值
	 *
	 * @param   enumObj
	 *          枚举对象
	 *
	 * @return  字符串
	 */
	public static String convertEnumToStr(Object enumObj) {
		Class<?> clazz = enumObj.getClass();
		if (!ClassUtil.isEnum(clazz)) {
			return null;
		}

		Field[] fields = ReflectUtil.getFields(clazz);
		for (Field field : fields) {
			boolean jsonValuePresent = field.isAnnotationPresent(JsonValue.class);
			boolean enumValuePresent = field.isAnnotationPresent(EnumValue.class);

			if (jsonValuePresent || enumValuePresent) {
				return Convert.toStr(ReflectUtil.getFieldValue(enumObj, field));
			}
		}

		return null;
	}

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/FileComparator.java
================================================
package im.zhaojun.zfile.core.util;

import cn.hutool.core.comparator.CompareUtil;
import im.zhaojun.zfile.module.storage.model.result.FileItemResult;
import im.zhaojun.zfile.module.storage.model.enums.FileTypeEnum;

import java.util.Comparator;

/**
 * 文件比较器
 * <ul>
 *     <li>文件夹始终比文件排序高</li>
 *     <li>默认按照名称排序</li>
 *     <li>默认排序为升序</li>
 *     <li>按名称排序不区分大小写</li>
 * </ul>
 * @author zhaojun
 */
public class FileComparator implements Comparator<FileItemResult> {

    private String sortBy;

    private String order;

    public FileComparator(String sortBy, String order) {
        this.sortBy = sortBy;
        this.order = order;
    }


    /**
     * 比较两个文件的大小
     *
     * @param   o1
     *          第一个文件
     *
     * @param   o2
     *          第二个文件
     *
     * @return  比较结果
     */
    @Override
    public int compare(FileItemResult o1, FileItemResult o2) {
        if (sortBy == null) {
            sortBy = "name";
        }

        if (order == null) {
            order = "asc";
        }
        FileTypeEnum o1Type = o1.getType();
        FileTypeEnum o2Type = o2.getType();
        NaturalOrderComparator naturalOrderComparator = new NaturalOrderComparator();
        if (o1Type.equals(o2Type)) {
            int result = switch (sortBy) {
                case "time" -> CompareUtil.compare(o1.getTime(), o2.getTime());
                case "size" -> CompareUtil.compare(o1.getSize(), o2.getSize());
                default -> naturalOrderComparator.compare(o1.getName(), o2.getName());
            };
            return "asc".equals(order) ? result : -result;
        }

        if (o1Type.equals(FileTypeEnum.FOLDER)) {
            return -1;
        } else {
            return 1;
        }
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/FileResponseUtil.java
================================================
package im.zhaojun.zfile.core.util;

import cn.hutool.core.io.FileUtil;
import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.status.NotFoundAccessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import java.io.File;
import java.nio.charset.StandardCharsets;

/**
 * 将文件输出对象
 *
 * @author zhaojun
 */
@Slf4j
public class FileResponseUtil {


    /**
     * 文件下载,单线程,不支持断点续传
     *
     * @param   file
     *          文件对象
     *
     * @param   fileName
     *          要保存为的文件名
     *
     * @return  文件下载对象
     */
    public static ResponseEntity<Resource> exportSingleThread(File file, String fileName) {
        if (!file.exists()) {
            throw new NotFoundAccessException(ErrorCode.BIZ_FILE_NOT_EXIST);
        }

        MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;

        HttpHeaders headers = new HttpHeaders();

        if (StringUtils.isEmpty(fileName)) {
            fileName = file.getName();
        }

        ContentDisposition contentDisposition = ContentDisposition
                .builder("inline")
                .filename(fileName, StandardCharsets.UTF_8)
                .build();
        headers.setContentDisposition(contentDisposition);

        return ResponseEntity
                .ok()
                .headers(headers)
                .contentLength(file.length())
                .contentType(mediaType)
                .body(new InputStreamResource(FileUtil.getInputStream(file)));
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/FileSizeConverter.java
================================================
package im.zhaojun.zfile.core.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FileSizeConverter {

    private static final long KB_FACTOR = 1024L;
    private static final long MB_FACTOR = 1024L * KB_FACTOR;
    private static final long GB_FACTOR = 1024L * MB_FACTOR;
    private static final long TB_FACTOR = 1024L * GB_FACTOR;
    private static final long PB_FACTOR = 1024L * TB_FACTOR;

    private static final Pattern FILE_SIZE_PATTERN = Pattern.compile("([\\d.]+)\\s*([a-zA-Z]+)");

    public static long convertFileSizeToBytes(String sizeStr) {
        if (sizeStr == null || sizeStr.trim().isEmpty()) {
            throw new IllegalArgumentException("输入字符串不能为空");
        }

        Matcher matcher = FILE_SIZE_PATTERN.matcher(sizeStr.trim());

        if (!matcher.matches()) {
            throw new IllegalArgumentException("无效的文件大小格式: " + sizeStr);
        }

        String valueStr = matcher.group(1);
        String unitStr = matcher.group(2).toUpperCase();

        double value;
        try {
            value = Double.parseDouble(valueStr);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("无效的数字格式: " + valueStr, e);
        }

        if (value < 0) {
            throw new IllegalArgumentException("文件大小不能为负数: " + valueStr);
        }

        long multiplier = switch (unitStr) {
            case "B" ->
                    1L;
            case "KB", "KIB" ->
                    KB_FACTOR;
            case "MB", "MIB" ->
                    MB_FACTOR;
            case "GB", "GIB" ->
                    GB_FACTOR;
            case "TB", "TIB" ->
                    TB_FACTOR;
            case "PB", "PIB" ->
                    PB_FACTOR;
            default -> throw new IllegalArgumentException("不支持的单位: " + unitStr + " (支持 B, KB, MB, GB, TB, PB)");
        };

        double bytesDouble = value * multiplier;
        if (bytesDouble > Long.MAX_VALUE) {
            throw new ArithmeticException("转换后的字节数超过了 Long 类型的最大值: " + bytesDouble);
        }

        return Math.round(bytesDouble);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/FileUtils.java
================================================
package im.zhaojun.zfile.core.util;

import org.apache.commons.io.FilenameUtils;

/**
 * 文件相关工具类
 *
 * @author zhaojun
 */
public class FileUtils {

    public static String getName(final String fileName) {
        if (fileName == null) {
            return null;
        }

        int i = fileName.lastIndexOf(CharSequenceUtil.SLASH_CHAR);
        if (i >= 0 && i <= fileName.length() - 1) {
            return fileName.substring(i + 1);
        }

        return fileName;
    }

    public static String getParentPath(final String fileName) {
        String fullPathNoEndSeparator = FilenameUtils.getFullPathNoEndSeparator(StringUtils.trimEndSlashes(fileName));
        if (fullPathNoEndSeparator == null || fullPathNoEndSeparator.isEmpty()) {
            return StringUtils.SLASH;
        }
        return fullPathNoEndSeparator;
    }

    public static String getExtension(final String fileName) throws IllegalArgumentException {
        if (fileName == null) {
            return null;
        }

        int i = fileName.lastIndexOf('.');
        if (i > 0 && i < fileName.length() - 1) {
            return fileName.substring(i + 1);
        }

        return "";
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/HttpUtil.java
================================================
package im.zhaojun.zfile.core.util;

import im.zhaojun.zfile.core.constant.ZFileConstant;
import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.biz.GetPreviewTextContentBizException;
import im.zhaojun.zfile.core.exception.core.BizException;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;

/**
 * 网络相关工具
 *
 * @author zhaojun
 */
@Slf4j
public class HttpUtil {

    /**
     * 获取 URL 对应的文件内容
     *
     * @param   url
     *          文件 URL
     *
     * @return  文件内容
     */
    public static String getTextContent(String url) {
        long maxFileSize = 1024 * ZFileConstant.TEXT_MAX_FILE_SIZE_KB;

        if (getRemoteFileSize(url) > maxFileSize) {
            throw new BizException(ErrorCode.BIZ_PREVIEW_FILE_SIZE_EXCEED);
        }

        String result;
        try {
            result = cn.hutool.http.HttpUtil.get(url);
        } catch (Exception e) {
            throw new GetPreviewTextContentBizException(url, e);
        }

        return result == null ? "" : result;
    }


    /**
     * 获取远程文件大小
     *
     * @param   url
     *          文件 URL
     *
     * @return  文件大小
     */
    public static Long getRemoteFileSize(String url) {
        long size = 0;
        URL urlObject;
        try {
            urlObject = new URL(url);
            URLConnection conn = urlObject.openConnection();
            size = conn.getContentLength();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return size;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/NaturalOrderComparator.java
================================================
package im.zhaojun.zfile.core.util;
/*
 NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in Java.
 Copyright (C) 2003 by Pierre-Luc Paour <natorder@paour.com>

 Based on the C version by Martin Pool, of which this is more or less a straight conversion.
 Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>

 This software is provided 'as-is', without any express or implied
 warranty.  In no event will the authors be held liable for any damages
 arising from the use of this software.

 Permission is granted to anyone to use this software for any purpose,
 including commercial applications, and to alter it and redistribute it
 freely, subject to the following restrictions:

 1. The origin of this software must not be misrepresented; you must not
 claim that you wrote the original software. If you use this software
 in a product, an acknowledgment in the product documentation would be
 appreciated but is not required.
 2. Altered source versions must be plainly marked as such, and must not be
 misrepresented as being the original software.
 3. This notice may not be removed or altered from any source distribution.
 */

import java.util.Comparator;

/**
 * 类 windows 文件排序算法
 *
 * @author zhaojun
 */
public class NaturalOrderComparator implements Comparator<String> {

    private static final char ZERO_CHAR = '0';

    private int compareRight(String a, String b) {
        int bias = 0, ia = 0, ib = 0;

        // The longest run of digits wins. That aside, the greatest
        // value wins, but we can't know that it will until we've scanned
        // both numbers to know that they have the same magnitude, so we
        // remember it in BIAS.
        for (; ; ia++, ib++) {
            char ca = charAt(a, ia);
            char cb = charAt(b, ib);

            if (!isDigit(ca) && !isDigit(cb)) {
                return bias;
            }
            if (!isDigit(ca)) {
                return -1;
            }
            if (!isDigit(cb)) {
                return +1;
            }
            if (ca == 0 && cb == 0) {
                return bias;
            }

            if (bias == 0) {
                if (ca < cb) {
                    bias = -1;
                } else if (ca > cb) {
                    bias = +1;
                }
            }
        }
    }

    @Override
    public int compare(String a, String b) {
        int ia = 0, ib = 0;
        int nza, nzb;
        char ca, cb;

        while (true) {
            // Only count the number of zeroes leading the last number compared
            nza = nzb = 0;

            ca = charAt(a, ia);
            cb = charAt(b, ib);

            // skip over leading spaces or zeros
            while (Character.isSpaceChar(ca) || ca == ZERO_CHAR) {
                if (ca == ZERO_CHAR) {
                    nza++;
                } else {
                    // Only count consecutive zeroes
                    nza = 0;
                }

                ca = charAt(a, ++ia);
            }

            while (Character.isSpaceChar(cb) || cb == '0') {
                if (cb == '0') {
                    nzb++;
                } else {
                    // Only count consecutive zeroes
                    nzb = 0;
                }

                cb = charAt(b, ++ib);
            }

            // Process run of digits
            if (Character.isDigit(ca) && Character.isDigit(cb)) {
                int bias = compareRight(a.substring(ia), b.substring(ib));
                if (bias != 0) {
                    return bias;
                }
            }

            if (ca == 0 && cb == 0) {
                // The strings compare the same. Perhaps the caller
                // will want to call strcmp to break the tie.
                return compareEqual(a, b, nza, nzb);
            }
            if (ca < cb) {
                return -1;
            }
            if (ca > cb) {
                return +1;
            }

            ++ia;
            ++ib;
        }
    }

    private static boolean isDigit(char c) {
        return Character.isDigit(c) || c == '.' || c == ',';
    }

    private static char charAt(String s, int i) {
        return i >= s.length() ? 0 : s.charAt(i);
    }

    private static int compareEqual(String a, String b, int nza, int nzb) {
        if (nza - nzb != 0) {
            return nza - nzb;
        }

        if (a.length() == b.length()) {
            return a.compareTo(b);
        }

        return a.length() - b.length();
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/NumberUtils.java
================================================
package im.zhaojun.zfile.core.util;

/**
 * 数字工具类
 *
 * @author zhaojun
 */
public class NumberUtils {

    public static boolean isNullOrZero(Integer number) {
        return number == null || number == 0;
    }

    public static boolean isNotNullOrZero(Integer number) {
        return number != null && number != 0;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/OnlyOfficeKeyCacheUtils.java
================================================
package im.zhaojun.zfile.core.util;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.CacheObj;
import im.zhaojun.zfile.module.onlyoffice.model.OnlyOfficeFile;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.RandomStringUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * OnlyOffice 文件信息与 Key 缓存工具类
 *
 * @author zhaojun
 */
@Slf4j
public class OnlyOfficeKeyCacheUtils {

    /**
     * 存储 OnlyOffice 文件信息与 Key 的映射关系. 最多存储 10000 个 Key, 防止内存溢出.
     */
    private static final Cache<OnlyOfficeFile, String> ONLY_OFFICE_FILE_KEY_MAP = CacheUtil.newLRUCache(10000);

    /**
     * 存储 OnlyOffice Key 与文件信息的映射关系. 最多存储 10000 个 Key, 防止内存溢出.
     */
    private static final Cache<String, OnlyOfficeFile> ONLY_OFFICE_KEY_FILE_MAP = CacheUtil.newLRUCache(10000);

    /**
     * 存储文件锁, 防止并发操作文件缓存时出现问题.
     */
    private static final Cache<OnlyOfficeFile, ReentrantLock> locks = CacheUtil.newLRUCache(300);

    /**
     * 获取该文件缓存的 key, 如果不存在则生成一个新的 key 并缓存.
     *
     * @param   onlyOfficeFile
     *          OnlyOffice 文件信息
     *
     * @return  该文件唯一标识
     */
    public static String getKeyOrPutNew(OnlyOfficeFile onlyOfficeFile, long timeout) {
        ReentrantLock lock = getLock(onlyOfficeFile);
        try {
            boolean getLock = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
            if (BooleanUtils.isFalse(getLock)) {
                log.warn("{} 尝试获取锁超时, 强制忽略锁直接操作文件.", onlyOfficeFile);
            }
            try {
                if (ONLY_OFFICE_FILE_KEY_MAP.containsKey(onlyOfficeFile)) {
                    return ONLY_OFFICE_FILE_KEY_MAP.get(onlyOfficeFile);
                } else {
                    String key = RandomStringUtils.randomAlphabetic(10);
                    ONLY_OFFICE_FILE_KEY_MAP.put(onlyOfficeFile, key);
                    ONLY_OFFICE_KEY_FILE_MAP.put(key, onlyOfficeFile);
                    return key;
                }
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IllegalStateException("Thread was interrupted", e);
        }

    }

    /**
     * 清理缓存中的 Key 与文件信息的映射关系.(文件发生了变化, 需要重新生成 OnlyOffice 预览链接时调用)
     *
     * @param   key
     *          文件唯一标识
     */
    public static OnlyOfficeFile removeByKey(String key) {
        OnlyOfficeFile onlyOfficeFile = ONLY_OFFICE_KEY_FILE_MAP.get(key);
        if (onlyOfficeFile == null) {
            return null;
        }
        ONLY_OFFICE_FILE_KEY_MAP.remove(onlyOfficeFile);
        ONLY_OFFICE_KEY_FILE_MAP.remove(key);
        return onlyOfficeFile;
    }

    /**
     * 清理缓存中的文件信息与 Key 的映射关系.(文件发生了变化, 需要重新生成 OnlyOffice 预览链接时调用)
     *
     * @param   onlyOfficeFile
     *          OnlyOffice 文件信息
     */
    public static OnlyOfficeFile removeByFile(OnlyOfficeFile onlyOfficeFile) {
        String key = ONLY_OFFICE_FILE_KEY_MAP.get(onlyOfficeFile);
        if (key == null) {
            return null;
        }
        ONLY_OFFICE_FILE_KEY_MAP.remove(onlyOfficeFile);
        ONLY_OFFICE_KEY_FILE_MAP.remove(key);
        return onlyOfficeFile;
    }


    /**
     * 清理缓存中的某个文件夹下所有文件信息与 Key 的映射关系.(文件发生了变化, 需要重新生成 OnlyOffice 预览链接时调用)
     *
     * @param   onlyOfficeFile
     *          OnlyOffice 文件信息
     */
    public static List<OnlyOfficeFile> removeByFolder(OnlyOfficeFile onlyOfficeFile) {
        List<OnlyOfficeFile> caches = new ArrayList<>();
        Iterator<CacheObj<OnlyOfficeFile, String>> cacheObjIterator = ONLY_OFFICE_FILE_KEY_MAP.cacheObjIterator();
        while (cacheObjIterator.hasNext()) {
            CacheObj<OnlyOfficeFile, String> cacheObj = cacheObjIterator.next();
            OnlyOfficeFile cacheOnlyOfficeFile = cacheObj.getKey();
            if (cacheOnlyOfficeFile.getStorageKey().equals(onlyOfficeFile.getStorageKey())
                    && StringUtils.startWith(cacheOnlyOfficeFile.getPathAndName(), onlyOfficeFile.getPathAndName())) {
                ONLY_OFFICE_FILE_KEY_MAP.remove(cacheObj.getKey());
                ONLY_OFFICE_KEY_FILE_MAP.remove(cacheObj.getValue());
                caches.add(cacheOnlyOfficeFile);
            }
        }
        return caches;
    }

    /**
     * 获取文件锁, 防止并发操作文件缓存时出现问题.
     *
     * @param   key
     *          文件唯一标识
     *
     * @return  锁对象
     */
    public static ReentrantLock getLock(OnlyOfficeFile key) {
        return locks.get(key, true, ReentrantLock::new);
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/PatternMatcherUtils.java
================================================
package im.zhaojun.zfile.core.util;

import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

/**
 * 规则表达式工具类
 *
 * @author zhaojun
 */
public class PatternMatcherUtils {
	
	private static final Map<String, PathMatcher> PATH_MATCHER_MAP = new HashMap<>();
	
	/**
	 * 兼容模式的 glob 表达式匹配.
	 * 默认的 glob 表达式是不支持以下情况的:<br>
	 * <ul>
	 * <li>pattern: /a/**</li>
	 * <li>test1: /a</li>
	 * <li>test2: /a/</li>
	 * <ul>
	 * <p>test1 和 test 2 均无法匹配这种情况, 此方法兼容了这种情况, 即对 test 内容后拼接 "/xx"(其实任意字符都可以), 使其可以匹配上 pattern.
	 * <p><strong>注意:</strong>但此方法对包含文件名的情况无效, 仅支持 test 为 路径的情况.
	 *
	 * @param 	pattern
	 *			glob 规则表达式
	 *
	 * @param 	test
	 *			匹配内容
	 *
	 * @return 	是否匹配.
	 */
	public static boolean testCompatibilityGlobPattern(String pattern, String test) {
		// 如果规则表达式最开始没有 /, 则兼容在最前方加上 /.
		if (!StringUtils.startWith(pattern, StringUtils.SLASH)) {
			pattern = StringUtils.SLASH + pattern;
		}
		
		// 兼容性处理.
		test = StringUtils.concat(test, StringUtils.SLASH);
		if (StringUtils.endWith(pattern, "/**") || StringUtils.endWith(pattern, "/*")) {
			test += "xxx";
		}
		return testGlobPattern(pattern, test);
	}
	
	
	/**
	 * 测试密码规则表达式和文件路径是否匹配
	 *
	 * @param 	pattern
	 *			glob 规则表达式
	 *
	 * @param 	test
	 *			测试字符串
	 */
	private static boolean testGlobPattern(String pattern, String test) {
		// 从缓存取出 PathMatcher, 防止重复初始化
		PathMatcher pathMatcher = PATH_MATCHER_MAP.getOrDefault(pattern, FileSystems.getDefault().getPathMatcher("glob:" + pattern));
		PATH_MATCHER_MAP.put(pattern, pathMatcher);
		
		return pathMatcher.matches(Paths.get(test)) || StringUtils.equals(pattern, test);
	}
	
}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/PlaceholderUtils.java
================================================
package im.zhaojun.zfile.core.util;

import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
 
/**
 * 配置文件或模板中的占位符替换工具类
 *
 * @author zhaojun
 */
@Slf4j
public class PlaceholderUtils {

    /**
     * Prefix for system property placeholders: "${"
     */
    public static final String PLACEHOLDER_PREFIX = "${";

    /**
     * Suffix for system property placeholders: "}"
     */
    public static final String PLACEHOLDER_SUFFIX = "}";


    /**
     * 解析占位符, 将指定的占位符替换为指定的值. 变量值从 Spring 环境中获取, 如没取到, 则默认为空.
     * <br>
     * 必须在 Spring 环境下使用, 否则会抛出异常.
     *
     *
     * @param   formatStr
     *          模板字符串
     *
     * @return  替换后的字符串
     */
    public static String resolvePlaceholdersBySpringProperties(String formatStr) {
        String placeholderName = getFirstPlaceholderName(formatStr);
        if (StringUtils.isEmpty(placeholderName)) {
            return formatStr;
        }

        String propertyValue = SpringUtil.getProperty(placeholderName);
        Map<String, String> map = new HashMap<>();
        map.put(placeholderName, propertyValue);
        return resolvePlaceholders(formatStr, map);
    }


    /**
     * 解析占位符, 将指定的占位符替换为指定的值.
     *
     * @param   formatStr
     *          模板字符串
     *
     * @param   parameter
     *          参数列表
     *
     * @return  替换后的字符串
     */
    public static String resolvePlaceholders(String formatStr, Map<String, String> parameter) {
        if (parameter == null || parameter.isEmpty()) {
            return formatStr;
        }
        StringBuilder sb = new StringBuilder(formatStr);
        int startIndex = sb.indexOf(PLACEHOLDER_PREFIX);
        while (startIndex != -1) {
            int endIndex = sb.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length());
            if (endIndex != -1) {
                String placeholder = sb.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
                int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length();
                try {
                    String propVal = parameter.get(placeholder);
                    if (propVal != null) {
                        sb.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal);
                        nextIndex = startIndex + propVal.length();
                    } else {
                        log.warn("Could not resolve placeholder '{}' in [{}] ", placeholder, formatStr);
                    }
                } catch (Exception ex) {
                    log.error("Could not resolve placeholder '{}' in [{}]: ", placeholder, formatStr, ex);
                }
                startIndex = sb.indexOf(PLACEHOLDER_PREFIX, nextIndex);
            } else {
                startIndex = -1;
            }
        }
        return sb.toString();
    }


    /**
     * 获取模板字符串第一个占位符的名称, 如 "我的名字是: ${name}, 我的年龄是: ${age}", 返回 "name".
     *
     * @param   formatStr
     *          模板字符串
     *
     * @return  占位符名称
     */
    public static String getFirstPlaceholderName(String formatStr) {
        List<String> list = getPlaceholderNames(formatStr);
        if (CollectionUtils.isNotEmpty(list)) {
            return list.getFirst();
        }
        return null;
    }


    /**
     * 获取模板字符串第一个占位符的名称, 如 "我的名字是: ${name}, 我的年龄是: ${age}", 返回 ["name", "age].
     *
     * @param   formatStr
     *          模板字符串
     *
     * @return  占位符名称
     */
    public static List<String> getPlaceholderNames(String formatStr) {
        if (StringUtils.isEmpty(formatStr)) {
            return Collections.emptyList();
        }

        List<String> placeholderNameList = new ArrayList<>();

        StringBuilder sb = new StringBuilder(formatStr);
        int startIndex = sb.indexOf(PLACEHOLDER_PREFIX);
        while (startIndex != -1) {
            int endIndex = sb.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length());
            if (endIndex != -1) {
                String placeholder = sb.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
                int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length();
                startIndex = sb.indexOf(PLACEHOLDER_PREFIX, nextIndex);
                placeholderNameList.add(placeholder);
            } else {
                startIndex = -1;
            }
        }
        return placeholderNameList;
    }

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/ProxyDownloadUrlUtils.java
================================================
package im.zhaojun.zfile.core.util;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.extra.spring.SpringUtil;
import im.zhaojun.zfile.module.config.service.SystemConfigService;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 代理下载链接工具类
 *
 * @author zhaojun
 */
@Slf4j
public class ProxyDownloadUrlUtils {

	private static SystemConfigService systemConfigService;

	private static final String PROXY_DOWNLOAD_LINK_DELIMITER = ":";

	private static final Map<String, SymmetricCrypto> AES_CACHE = new HashMap<>();

	/**
	 * 服务器代理下载 URL 有效期 (秒).
	 */
	public static final Integer PROXY_DOWNLOAD_LINK_EFFECTIVE_SECOND = 1800;

	/**
	 * 生成签名:根据系统设置中 AES 密钥对生成签名.
	 *
	 * @param 	storageId
	 * 			存储源 ID
	 *
	 * @param 	pathAndName
	 * 			文件路径及文件名称
	 *
	 * @param 	effectiveSecond
	 * 			有效时间, 单位: 秒
	 *
	 * @return	签名
	 */
	public static String generatorSignature(Integer storageId, String pathAndName, Integer effectiveSecond) {
		if (systemConfigService == null) {
			systemConfigService = SpringUtil.getBean(SystemConfigService.class);
		}

		// 如果有效时间为空, 则设置 30 分钟过期
		if (effectiveSecond == null || effectiveSecond < 1) {
			effectiveSecond = PROXY_DOWNLOAD_LINK_EFFECTIVE_SECOND;
		}

		// 过期时间的秒数
		long second = DateUtil.offsetSecond(DateUtil.date(), effectiveSecond).getTime();
		String content = storageId + PROXY_DOWNLOAD_LINK_DELIMITER + pathAndName + PROXY_DOWNLOAD_LINK_DELIMITER + second;

		String aesHexKey = systemConfigService.getAesHexKeyOrGenerate();
		SymmetricCrypto aes = AES_CACHE.computeIfAbsent(aesHexKey, k -> new SymmetricCrypto(SymmetricAlgorithm.AES, HexUtil.decodeHex(k)));

		//加密
		return aes.encryptHex(content);
	}


	public static boolean validSignatureExpired(Integer expectedStorageId, String expectedPathAndName, String signature) {
		if (systemConfigService == null) {
			systemConfigService = SpringUtil.getBean(SystemConfigService.class);
		}

		String aesHexKey = systemConfigService.getAesHexKeyOrGenerate();
		SymmetricCrypto aes = AES_CACHE.computeIfAbsent(aesHexKey, k -> new SymmetricCrypto(SymmetricAlgorithm.AES, HexUtil.decodeHex(k)));

		long currentTimeMillis = System.currentTimeMillis();
		
		String storageId = null;
		String pathAndName = null;
		String expiredSecond = null;
		
		try {
			//解密
			String decryptStr = aes.decryptStr(signature);
			List<String> split = StringUtils.split(decryptStr, PROXY_DOWNLOAD_LINK_DELIMITER);
			storageId = split.get(0);
			pathAndName = split.get(1);
			expiredSecond = split.get(2);
			
			// 校验存储源 ID 和文件路径及是否过期.
			if (StringUtils.equals(storageId, Convert.toStr(expectedStorageId))
				&& StringUtils.equals(StringUtils.concat(pathAndName), StringUtils.concat(expectedPathAndName))
				&& currentTimeMillis < Convert.toLong(expiredSecond)) {
				return true;
			}
			
			log.warn("校验链接已过期或不匹配, signature: {}, storageId={}, pathAndName={}, expiredSecond={}, now:={}", signature, storageId, pathAndName, expiredSecond, currentTimeMillis);
		} catch (Exception e) {
			log.error("校验签名链接异常, signature: {}, storageId={}, pathAndName={}, expiredSecond={}, now:={}", signature, storageId, pathAndName, expiredSecond, currentTimeMillis);
			return false;
		}

		return false;
	}

}

================================================
FILE: src/main/java/im/zhaojun/zfile/core/util/RequestHolder.java
================================================
package im.zhaojun.zfile.core.util;

import cn.hutool.extra.servlet.JakartaServletUtil;
import im.zhaojun.zfile.core.constant.ZFileHttpHeaderConstant;
import im.zhaojun.zfile.core.exception.ErrorCode;
import im.zhaojun.zfile.core.exception.core.BizException;
import im.zhaojun.zfile.core.exception.core.SystemException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRange;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.util.StreamUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 获取 Request 工具类
 *
 * @author zhaojun
 */
@Slf4j
public class RequestHolder {

    /**
     * 获取 HttpServletRequest
     *
     * @return HttpServletRequest
     */
    public static HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    }


    /**
     * 获取 HttpServletResponse
     *
     * @return HttpServletResponse
     */
    public static HttpServletResponse getResponse() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
    }


    /**
     * 向 response 写入文件流.
     *
     * @param   inputStream
     *          文件输入流
     *
     * @param   fileName
     *          文件名称
     *
     * @param   fileSize
     *          文件大小
     *
     * @param   isPartialContentFromInputStream
     *          表示输入流是否为部分内容。
     *          当该变量为 true 时,表示输入流已经根据 range 规则从存储源获取部分内容。
     *          在这种情况下,不需要跳过 range start 部分,可以直接从输入流的全部内容复制到输出流。
     *
     * @param   forceDownload
     *          是否强制下载
     */
    public static void writeFile(InputStream inputStream, String fileName, Long fileSize, boolean isPartialContentFromInputStream, boolean forceDownload) {
        if (inputStream == null) {
            throw new BizException(ErrorCode.BIZ_FILE_NOT_EXIST);
        }
        OutputStream outputStream = null;
        try (InputStream innerInputStream = inputStream) {
            HttpServletResponse response = RequestHolder.getResponse();

            ContentDisposition contentDisposition = ContentDisposition
                    .builder(forceDownload ? "attachment" : "inline")
                    .filename(fileName, StandardCharsets.UTF_8)
                    .build();
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
            if (forceDownload) {
                response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            } else {
                response.setContentType(MediaTypeFactory.getMediaType(fileName).orElse(MediaType.APPLICATION_OCTET_STREAM).toString());
            }

            outputStream = response.getOutputStream();

            if (fileSize != null && fileSize > 0) {
                String range = RequestHolder.getRequest().getHeader(HttpHeaders.RANGE);
                List<HttpRange> httpRanges = HttpRange.parseRanges(range);
                if (httpRanges.isEmpty()) {
                    httpRanges = Collections.singletonList(HttpRange.createByteRange(0, fileSize - 1));
                } else {
                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                }
                HttpRange httpRange = CollectionUtils.getFirst(httpRanges);
                long startPos = httpRange.getRangeStart(fileSize);
                long endPos = httpRange.getRangeEnd(fileSize);
                if (response.getStatus() == HttpServletResponse.SC_PARTIAL_CONTENT) {
                    response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + startPos + "-" + endPos + StringUtils.SLASH + fileSize);
                }

                response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
                response.setContentLengthLong(endPos - startPos + 1);
                if (isPartialContentFromInputStream) {
                    StreamUtils.copy(innerInputStream, outputStream);
                } else {
                    StreamUtils.copyRange(innerInputStream, outputStream, startPos, endPos);
                }
                return;
            }

            StreamUtils.copy(innerInputStream, outputStream);
        } catch (IOException e) {
            boolean isBrokenPipe = e.getMessage().contains("Broken pipe");
            boolean isConnectionResetByPeer = e.getMessage().contains("Connection reset by peer");
            if (isBrokenPipe || isConnectionResetByPeer) {
   
Download .txt
gitextract_wuofd07t/

├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.yml
│       └── config.yml
├── .gitignore
├── .package/
│   └── script/
│       ├── log.sh
│       ├── restart.sh
│       ├── start.sh
│       ├── status.sh
│       ├── stop.sh
│       └── 双击我启动.bat
├── Dockerfile
├── LICENSE
├── README.md
├── pom.xml
└── src/
    └── main/
        ├── java/
        │   └── im/
        │       └── zhaojun/
        │           └── zfile/
        │               ├── ZfileApplication.java
        │               ├── core/
        │               │   ├── annotation/
        │               │   │   ├── ApiLimit.java
        │               │   │   └── DemoDisable.java
        │               │   ├── aspect/
        │               │   │   ├── ApiLimitAspect.java
        │               │   │   ├── CommonResultControllerAdvice.java
        │               │   │   └── DemoDisableAspect.java
        │               │   ├── cache/
        │               │   │   └── ZFileCacheManager.java
        │               │   ├── config/
        │               │   │   ├── ZFileProperties.java
        │               │   │   ├── datasource/
        │               │   │   │   └── DataSourceBeanPostProcessor.java
        │               │   │   ├── docs/
        │               │   │   │   └── Knife4jConfiguration.java
        │               │   │   ├── jackson/
        │               │   │   │   ├── JSONStringDeserializer.java
        │               │   │   │   └── JSONStringSerializer.java
        │               │   │   ├── mybatis/
        │               │   │   │   ├── CollectionIntegerTypeHandler.java
        │               │   │   │   ├── CollectionStrTypeHandler.java
        │               │   │   │   ├── CollectionTypeHandler.java
        │               │   │   │   ├── MyBatisPlusConfig.java
        │               │   │   │   ├── MyDatabaseIdProvider.java
        │               │   │   │   └── MyMetaObjectHandler.java
        │               │   │   ├── security/
        │               │   │   │   ├── SaSessionForJacksonCustomized.java
        │               │   │   │   ├── SaTokenConfigure.java
        │               │   │   │   ├── SaTokenDaoRedisJackson.java
        │               │   │   │   └── StpInterfaceImpl.java
        │               │   │   ├── spring/
        │               │   │   │   ├── JacksonEnumDeserializer.java
        │               │   │   │   ├── SpringCacheConfig.java
        │               │   │   │   ├── StringToEnumConverterFactory.java
        │               │   │   │   └── WebMvcConfig.java
        │               │   │   └── totp/
        │               │   │       ├── TotpAutoConfiguration.java
        │               │   │       └── TotpProperties.java
        │               │   ├── constant/
        │               │   │   ├── MdcConstant.java
        │               │   │   ├── RuleTypeConstant.java
        │               │   │   ├── ZFileConstant.java
        │               │   │   └── ZFileHttpHeaderConstant.java
        │               │   ├── controller/
        │               │   │   ├── FrontIndexController.java
        │               │   │   └── LogController.java
        │               │   ├── exception/
        │               │   │   ├── ErrorCode.java
        │               │   │   ├── GlobalExceptionHandler.java
        │               │   │   ├── biz/
        │               │   │   │   ├── APIHttpRequestBizException.java
        │               │   │   │   ├── CorsBizException.java
        │               │   │   │   ├── FilePathSecurityBizException.java
        │               │   │   │   ├── GetPreviewTextContentBizException.java
        │               │   │   │   ├── InitializeStorageSourceBizException.java
        │               │   │   │   ├── InvalidStorageSourceBizException.java
        │               │   │   │   ├── StorageSourceFileForbiddenAccessBizException.java
        │               │   │   │   └── StorageSourceIllegalOperationBizException.java
        │               │   │   ├── core/
        │               │   │   │   ├── BizException.java
        │               │   │   │   ├── ErrorPageBizException.java
        │               │   │   │   └── SystemException.java
        │               │   │   ├── status/
        │               │   │   │   ├── BadRequestAccessException.java
        │               │   │   │   ├── ForbiddenAccessException.java
        │               │   │   │   ├── MethodNotAllowedAccessException.java
        │               │   │   │   ├── NotFoundAccessException.java
        │               │   │   │   └── UnauthorizedAccessException.java
        │               │   │   └── system/
        │               │   │       ├── UploadFileFailSystemException.java
        │               │   │       └── ZFileAuthorizationSystemException.java
        │               │   ├── filter/
        │               │   │   ├── CorsFilter.java
        │               │   │   ├── MDCFilter.java
        │               │   │   └── SecurityFilter.java
        │               │   ├── io/
        │               │   │   └── EnsureContentLengthInputStreamResource.java
        │               │   ├── model/
        │               │   │   └── request/
        │               │   │       └── PageQueryRequest.java
        │               │   ├── util/
        │               │   │   ├── AjaxJson.java
        │               │   │   ├── ArrayUtils.java
        │               │   │   ├── CharPool.java
        │               │   │   ├── CharSequenceUtil.java
        │               │   │   ├── ClassUtils.java
        │               │   │   ├── CollectionUtils.java
        │               │   │   ├── DnsUtil.java
        │               │   │   ├── EnumConvertUtils.java
        │               │   │   ├── FileComparator.java
        │               │   │   ├── FileResponseUtil.java
        │               │   │   ├── FileSizeConverter.java
        │               │   │   ├── FileUtils.java
        │               │   │   ├── HttpUtil.java
        │               │   │   ├── NaturalOrderComparator.java
        │               │   │   ├── NumberUtils.java
        │               │   │   ├── OnlyOfficeKeyCacheUtils.java
        │               │   │   ├── PatternMatcherUtils.java
        │               │   │   ├── PlaceholderUtils.java
        │               │   │   ├── ProxyDownloadUrlUtils.java
        │               │   │   ├── RequestHolder.java
        │               │   │   ├── RequestUtils.java
        │               │   │   ├── SizeToStrUtils.java
        │               │   │   ├── SpringMvcUtils.java
        │               │   │   ├── StrPool.java
        │               │   │   ├── StringUtils.java
        │               │   │   ├── UrlUtils.java
        │               │   │   ├── ZFileAuthUtil.java
        │               │   │   └── matcher/
        │               │   │       ├── AbstractRuleMatcher.java
        │               │   │       ├── IRuleMatcher.java
        │               │   │       ├── RuleMatcherFactory.java
        │               │   │       └── impl/
        │               │   │           ├── AntPathRuleMatcher.java
        │               │   │           ├── IpRuleMatcher.java
        │               │   │           ├── RegexRuleMatcher.java
        │               │   │           └── SpringSimpleRuleMatcher.java
        │               │   └── validation/
        │               │       ├── StringListValue.java
        │               │       └── StringListValueConstraintValidator.java
        │               └── module/
        │                   ├── admin/
        │                   │   ├── controller/
        │                   │   │   ├── IpHelperController.java
        │                   │   │   └── RuleMatcherTestController.java
        │                   │   └── model/
        │                   │       └── request/
        │                   │           └── TestRuleMatcherRequest.java
        │                   ├── config/
        │                   │   ├── annotation/
        │                   │   │   └── JSONStringParse.java
        │                   │   ├── constant/
        │                   │   │   └── SystemConfigConstant.java
        │                   │   ├── controller/
        │                   │   │   ├── SettingController.java
        │                   │   │   └── SiteController.java
        │                   │   ├── event/
        │                   │   │   ├── DirectLinkPrefixModifyHandler.java
        │                   │   │   ├── ISystemConfigModifyHandler.java
        │                   │   │   ├── SecureLoginEntryModifyHandler.java
        │                   │   │   └── SystemConfigModifyHandlerChain.java
        │                   │   ├── mapper/
        │                   │   │   └── SystemConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── dto/
        │                   │   │   │   ├── LinkExpireDTO.java
        │                   │   │   │   └── SystemConfigDTO.java
        │                   │   │   ├── entity/
        │                   │   │   │   └── SystemConfig.java
        │                   │   │   ├── enums/
        │                   │   │   │   └── FileClickModeEnum.java
        │                   │   │   ├── request/
        │                   │   │   │   ├── UpdateAccessSettingRequest.java
        │                   │   │   │   ├── UpdateLinkSettingRequest.java
        │                   │   │   │   ├── UpdateSecuritySettingRequest.java
        │                   │   │   │   ├── UpdateSiteSettingRequest.java
        │                   │   │   │   ├── UpdateUserNameAndPasswordRequest.java
        │                   │   │   │   └── UpdateViewSettingRequest.java
        │                   │   │   └── result/
        │                   │   │       └── FrontSiteConfigResult.java
        │                   │   └── service/
        │                   │       └── SystemConfigService.java
        │                   ├── filter/
        │                   │   ├── controller/
        │                   │   │   └── StorageSourceFilterController.java
        │                   │   ├── mapper/
        │                   │   │   └── FilterConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   └── FilterConfig.java
        │                   │   │   └── enums/
        │                   │   │       └── FilterConfigHiddenModeEnum.java
        │                   │   └── service/
        │                   │       └── FilterConfigService.java
        │                   ├── install/
        │                   │   ├── controller/
        │                   │   │   └── InstallController.java
        │                   │   ├── model/
        │                   │   │   └── request/
        │                   │   │       └── InstallSystemRequest.java
        │                   │   └── service/
        │                   │       └── InstallService.java
        │                   ├── link/
        │                   │   ├── aspect/
        │                   │   │   ├── LinkRateLimiterAspect.java
        │                   │   │   └── RefererCheckAspect.java
        │                   │   ├── cache/
        │                   │   │   └── LinkRateLimiterCache.java
        │                   │   ├── controller/
        │                   │   │   ├── DirectLinkController.java
        │                   │   │   ├── ShortLinkController.java
        │                   │   │   └── ShortLinkManagerController.java
        │                   │   ├── convert/
        │                   │   │   └── ShortLinkConvert.java
        │                   │   ├── dto/
        │                   │   │   └── DynamicRegisterMappingHandlerDTO.java
        │                   │   ├── event/
        │                   │   │   └── DeleteExpireLinkEvent.java
        │                   │   ├── mapper/
        │                   │   │   └── ShortLinkMapper.java
        │                   │   ├── model/
        │                   │   │   ├── dto/
        │                   │   │   │   └── CacheInfo.java
        │                   │   │   ├── entity/
        │                   │   │   │   └── ShortLink.java
        │                   │   │   ├── enums/
        │                   │   │   │   └── RefererTypeEnum.java
        │                   │   │   ├── request/
        │                   │   │   │   ├── BatchDeleteRequest.java
        │                   │   │   │   ├── BatchGenerateLinkRequest.java
        │                   │   │   │   ├── QueryDownloadLogRequest.java
        │                   │   │   │   ├── QueryLoginLogRequest.java
        │                   │   │   │   ├── QueryShortLinkLogRequest.java
        │                   │   │   │   ├── ShortLinkResult.java
        │                   │   │   │   └── ShortLinkSearchRequest.java
        │                   │   │   └── result/
        │                   │   │       └── BatchGenerateLinkResponse.java
        │                   │   └── service/
        │                   │       ├── DynamicDirectLinkPrefixService.java
        │                   │       ├── LinkDownloadService.java
        │                   │       └── ShortLinkService.java
        │                   ├── log/
        │                   │   ├── controller/
        │                   │   │   ├── DownloadLogManagerController.java
        │                   │   │   └── LoginLogController.java
        │                   │   ├── convert/
        │                   │   │   └── DownloadLogConvert.java
        │                   │   ├── mapper/
        │                   │   │   ├── DownloadLogMapper.java
        │                   │   │   └── LoginLogMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   ├── DownloadLog.java
        │                   │   │   │   └── LoginLog.java
        │                   │   │   └── result/
        │                   │   │       └── DownloadLogResult.java
        │                   │   └── service/
        │                   │       ├── DownloadLogService.java
        │                   │       └── LoginLogService.java
        │                   ├── onlyoffice/
        │                   │   ├── controller/
        │                   │   │   └── OnlyOfficeController.java
        │                   │   └── model/
        │                   │       ├── OnlyOfficeCallback.java
        │                   │       └── OnlyOfficeFile.java
        │                   ├── password/
        │                   │   ├── controller/
        │                   │   │   └── StorageSourcePasswordController.java
        │                   │   ├── mapper/
        │                   │   │   └── PasswordConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── dto/
        │                   │   │   │   └── VerifyResultDTO.java
        │                   │   │   └── entity/
        │                   │   │       └── PasswordConfig.java
        │                   │   └── service/
        │                   │       └── PasswordConfigService.java
        │                   ├── permission/
        │                   │   ├── controller/
        │                   │   │   ├── PermissionController.java
        │                   │   │   └── StorageSourcePermissionController.java
        │                   │   ├── convert/
        │                   │   │   └── PermissionConfigConvert.java
        │                   │   ├── mapper/
        │                   │   │   └── PermissionConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   └── PermissionConfig.java
        │                   │   │   └── result/
        │                   │   │       ├── PermissionConfigResult.java
        │                   │   │       └── PermissionInfoResult.java
        │                   │   └── service/
        │                   │       └── PermissionConfigService.java
        │                   ├── readme/
        │                   │   ├── controller/
        │                   │   │   └── StorageSourceReadmeController.java
        │                   │   ├── mapper/
        │                   │   │   └── ReadmeConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   └── ReadmeConfig.java
        │                   │   │   └── enums/
        │                   │   │       ├── ReadmeDisplayModeEnum.java
        │                   │   │       └── ReadmePathModeEnum.java
        │                   │   └── service/
        │                   │       └── ReadmeConfigService.java
        │                   ├── share/
        │                   │   ├── context/
        │                   │   │   └── ShareAccessContext.java
        │                   │   ├── controller/
        │                   │   │   ├── ShareFileManagerController.java
        │                   │   │   └── ShareLinkController.java
        │                   │   ├── mapper/
        │                   │   │   └── ShareLinkMapper.java
        │                   │   ├── model/
        │                   │   │   ├── dto/
        │                   │   │   │   └── ShareEntryDTO.java
        │                   │   │   ├── entity/
        │                   │   │   │   └── ShareLink.java
        │                   │   │   ├── enums/
        │                   │   │   │   ├── ShareEntryTypeEnum.java
        │                   │   │   │   └── ShareTypeEnum.java
        │                   │   │   ├── request/
        │                   │   │   │   ├── CreateShareLinkRequest.java
        │                   │   │   │   ├── ShareFileListRequest.java
        │                   │   │   │   ├── ShareLinkListRequest.java
        │                   │   │   │   └── VerifySharePasswordRequest.java
        │                   │   │   └── result/
        │                   │   │       ├── CreateShareLinkResult.java
        │                   │   │       ├── ShareFileInfoResult.java
        │                   │   │       └── ShareLinkResult.java
        │                   │   └── service/
        │                   │       ├── ShareLinkFileService.java
        │                   │       └── ShareLinkService.java
        │                   ├── sso/
        │                   │   ├── controller/
        │                   │   │   ├── SsoAPIController.java
        │                   │   │   ├── SsoController.java
        │                   │   │   └── SsoManagerController.java
        │                   │   ├── mapper/
        │                   │   │   └── SsoConfigMapper.java
        │                   │   ├── model/
        │                   │   │   ├── entity/
        │                   │   │   │   └── SsoConfig.java
        │                   │   │   ├── request/
        │                   │   │   │   └── CheckProviderDuplicateRequest.java
        │                   │   │   └── response/
        │                   │   │       ├── SsoLoginItemResponse.java
        │                   │   │       └── TokenResponse.java
        │                   │   └── service/
        │                   │       └── SsoService.java
        │                   ├── storage/
        │                   │   ├── annotation/
        │                   │   │   ├── CheckPassword.java
        │                   │   │   ├── CheckPasswords.java
        │                   │   │   ├── LinkRateLimiter.java
        │                   │   │   ├── ProCheck.java
        │                   │   │   ├── RefererCheck.java
        │                   │   │   ├── StorageParamItem.java
        │                   │   │   ├── StorageParamSelect.java
        │                   │   │   ├── StorageParamSelectOption.java
        │                   │   │   ├── StoragePermissionCheck.java
        │                   │   │   └── impl/
        │                   │   │       └── EncodingStorageParamSelect.java
        │                   │   ├── aspect/
        │                   │   │   ├── CheckPasswordAspect.java
        │                   │   │   └── FileOperatorCheckAspect.java
        │                   │   ├── chain/
        │                   │   │   ├── FileChain.java
        │                   │   │   ├── FileContext.java
        │                   │   │   └── command/
        │                   │   │       ├── FileAccessPermissionVerifyCommand.java
        │                   │   │       ├── FileDownloadPermissionCommand.java
        │                   │   │       ├── FileHiddenCommand.java
        │                   │   │       ├── FileSortCommand.java
        │                   │   │       └── FolderPasswordVerifyCommand.java
        │                   │   ├── constant/
        │                   │   │   ├── S3SignerTypeConstant.java
        │                   │   │   ├── StorageConfigConstant.java
        │                   │   │   └── StorageSourceConnectionProperties.java
        │                   │   ├── context/
        │                   │   │   ├── StorageSourceContext.java
        │                   │   │   └── StorageSourceInitializer.java
        │                   │   ├── controller/
        │                   │   │   ├── base/
        │                   │   │   │   ├── StorageMetaDataController.java
        │                   │   │   │   └── StorageSourceController.java
        │                   │   │   ├── callback/
        │                   │   │   │   ├── GoogleDriveCallbackController.java
        │                   │   │   │   └── OneDriveCallbackController.java
        │                   │   │   ├── file/
        │                   │   │   │   ├── FileController.java
        │                   │   │   │   └── FileOperatorController.java
        │                   │   │   ├── helper/
        │                   │   │   │   ├── GoogleDriveHelperController.java
        │                   │   │   │   ├── Open115HelperController.java
        │                   │   │   │   ├── Open115UploadUtils.java
        │                   │   │   │   ├── S3HelperController.java
        │                   │   │   │   └── SharePointHelperController.java
        │                   │   │   └── proxy/
        │                   │   │       ├── Open115UrlController.java
        │                   │   │       ├── ProxyDownloadController.java
        │                   │   │       └── ProxyUploadController.java
        │                   │   ├── convert/
        │                   │   │   └── StorageSourceConvert.java
        │                   │   ├── enums/
        │                   │   │   └── StorageParamItemAnnoEnum.java
        │                   │   ├── event/
        │                   │   │   ├── StorageSourceCopyEvent.java
        │                   │   │   └── StorageSourceDeleteEvent.java
        │                   │   ├── function/
        │                   │   │   ├── AllowAdminFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── AllowAllFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── BasicFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── DisableAllFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── LinkFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   ├── SearchFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   │   └── ShortLinkFileOperatorTypeEnumDefaultValueFunc.java
        │                   │   ├── mapper/
        │                   │   │   ├── StorageSourceConfigMapper.java
        │                   │   │   └── StorageSourceMapper.java
        │                   │   ├── model/
        │                   │   │   ├── bo/
        │                   │   │   │   ├── AuthModel.java
        │                   │   │   │   ├── RefreshTokenCacheBO.java
        │                   │   │   │   ├── StorageSourceMetadata.java
        │                   │   │   │   ├── StorageSourceParamDef.java
        │                   │   │   │   └── UploadSignParam.java
        │                   │   │   ├── dto/
        │                   │   │   │   ├── FileOperatorTypeDefaultValueDTO.java
        │                   │   │   │   ├── OAuth2TokenDTO.java
        │                   │   │   │   ├── RefreshTokenInfoDTO.java
        │                   │   │   │   ├── StorageSourceAllParamDTO.java
        │                   │   │   │   ├── StorageSourceDTO.java
        │                   │   │   │   ├── StorageSourceInitDTO.java
        │                   │   │   │   └── ZFileCORSRule.java
        │                   │   │   ├── entity/
        │                   │   │   │   ├── StorageSource.java
        │                   │   │   │   └── StorageSourceConfig.java
        │                   │   │   ├── enums/
        │                   │   │   │   ├── FileOperatorTypeEnum.java
        │                   │   │   │   ├── FileTypeEnum.java
        │                   │   │   │   ├── SearchFolderModeEnum.java
        │                   │   │   │   ├── SearchModeEnum.java
        │                   │   │   │   ├── StorageParamTypeEnum.java
        │                   │   │   │   └── StorageTypeEnum.java
        │                   │   │   ├── param/
        │                   │   │   │   ├── AliyunParam.java
        │                   │   │   │   ├── DogeCloudParam.java
        │                   │   │   │   ├── FtpParam.java
        │                   │   │   │   ├── GoogleDriveParam.java
        │                   │   │   │   ├── HuaweiParam.java
        │                   │   │   │   ├── IStorageParam.java
        │                   │   │   │   ├── LocalParam.java
        │                   │   │   │   ├── MicrosoftDriveParam.java
        │                   │   │   │   ├── MinIOParam.java
        │                   │   │   │   ├── OneDriveChinaParam.java
        │                   │   │   │   ├── OneDriveParam.java
        │                   │   │   │   ├── Open115Param.java
        │                   │   │   │   ├── OptionalProxyTransferParam.java
        │                   │   │   │   ├── ProxyTransferParam.java
        │                   │   │   │   ├── QiniuParam.java
        │                   │   │   │   ├── S3BaseParam.java
        │                   │   │   │   ├── S3Param.java
        │                   │   │   │   ├── SftpParam.java
        │                   │   │   │   ├── SharePointChinaParam.java
        │                   │   │   │   ├── SharePointParam.java
        │                   │   │   │   ├── TencentParam.java
        │                   │   │   │   ├── UpYunParam.java
        │                   │   │   │   └── WebdavParam.java
        │                   │   │   ├── request/
        │                   │   │   │   ├── GetGoogleDriveListRequest.java
        │                   │   │   │   ├── GetS3BucketListRequest.java
        │                   │   │   │   ├── GetS3CorsListRequest.java
        │                   │   │   │   ├── SharePointInfoRequest.java
        │                   │   │   │   ├── SharePointSearchSitesRequest.java
        │                   │   │   │   ├── SharePointSiteListsRequest.java
        │                   │   │   │   ├── admin/
        │                   │   │   │   │   ├── CopyStorageSourceRequest.java
        │                   │   │   │   │   ├── UpdateStorageIdRequest.java
        │                   │   │   │   │   └── UpdateStorageSortRequest.java
        │                   │   │   │   ├── base/
        │                   │   │   │   │   ├── FileItemRequest.java
        │                   │   │   │   │   ├── FileListConfigRequest.java
        │                   │   │   │   │   ├── FileListRequest.java
        │                   │   │   │   │   ├── SaveStorageSourceRequest.java
        │                   │   │   │   │   └── SearchStorageRequest.java
        │                   │   │   │   └── operator/
        │                   │   │   │       ├── BatchDeleteRequest.java
        │                   │   │   │       ├── BatchMoveOrCopyFileRequest.java
        │                   │   │   │       ├── NewFolderRequest.java
        │                   │   │   │       ├── RenameFileRequest.java
        │                   │   │   │       ├── RenameFolderRequest.java
        │                   │   │   │       └── UploadFileRequest.java
        │                   │   │   └── result/
        │                   │   │       ├── FileInfoResult.java
        │                   │   │       ├── FileItemResult.java
        │                   │   │       ├── GoogleDriveInfoResult.java
        │                   │   │       ├── Open115AuthDeviceCodeResult.java
        │                   │   │       ├── Open115GetStatusResult.java
        │                   │   │       ├── S3BucketNameResult.java
        │                   │   │       ├── SharepointSiteListResult.java
        │                   │   │       ├── SharepointSiteResult.java
        │                   │   │       ├── StorageSourceAdminResult.java
        │                   │   │       ├── StorageSourceConfigResult.java
        │                   │   │       ├── StorageSourceResult.java
        │                   │   │       └── operator/
        │                   │   │           └── BatchOperatorResult.java
        │                   │   ├── oauth2/
        │                   │   │   └── service/
        │                   │   │       ├── AbstractMicrosoftOAuth2Service.java
        │                   │   │       ├── GoogleDriveOAuth2ServiceImpl.java
        │                   │   │       ├── IOAuth2Service.java
        │                   │   │       ├── OneDriveChinaOAuth2ServiceImpl.java
        │                   │   │       └── OneDriveOAuth2ServiceImpl.java
        │                   │   ├── service/
        │                   │   │   ├── StorageSourceConfigService.java
        │                   │   │   ├── StorageSourceService.java
        │                   │   │   ├── base/
        │                   │   │   │   ├── AbstractBaseFileService.java
        │                   │   │   │   ├── AbstractMicrosoftDriveService.java
        │                   │   │   │   ├── AbstractOneDriveServiceBase.java
        │                   │   │   │   ├── AbstractProxyTransferService.java
        │                   │   │   │   ├── AbstractS3BaseFileService.java
        │                   │   │   │   ├── AbstractSharePointServiceBase.java
        │                   │   │   │   ├── BaseFileService.java
        │                   │   │   │   └── RefreshTokenService.java
        │                   │   │   └── impl/
        │                   │   │       ├── AliyunServiceImpl.java
        │                   │   │       ├── DogeCloudServiceImpl.java
        │                   │   │       ├── FtpServiceImpl.java
        │                   │   │       ├── GoogleDriveServiceImpl.java
        │                   │   │       ├── HuaweiServiceImpl.java
        │                   │   │       ├── LocalServiceImpl.java
        │                   │   │       ├── MinIOServiceImpl.java
        │                   │   │       ├── OneDriveChinaServiceImpl.java
        │                   │   │       ├── OneDriveServiceImpl.java
        │                   │   │       ├── Open115ServiceImpl.java
        │                   │   │       ├── QiniuServiceImpl.java
        │                   │   │       ├── S3ServiceImpl.java
        │                   │   │       ├── SftpServiceImpl.java
        │                   │   │       ├── SharePointChinaServiceImpl.java
        │                   │   │       ├── SharePointServiceImpl.java
        │                   │   │       ├── TencentServiceImpl.java
        │                   │   │       ├── UpYunServiceImpl.java
        │                   │   │       └── WebdavServiceImpl.java
        │                   │   └── support/
        │                   │       ├── Open115IdCacheService.java
        │                   │       ├── StorageSourceSupport.java
        │                   │       ├── ftp/
        │                   │       │   ├── FtpClientFactory.java
        │                   │       │   └── FtpClientPool.java
        │                   │       ├── sftp/
        │                   │       │   ├── SFtpClientFactory.java
        │                   │       │   └── SFtpClientPool.java
        │                   │       └── webdav/
        │                   │           └── CustomSardine.java
        │                   └── user/
        │                       ├── aspect/
        │                       │   └── LoginLogAspect.java
        │                       ├── controller/
        │                       │   ├── AdminTwoFAController.java
        │                       │   ├── UserController.java
        │                       │   └── UserManagerController.java
        │                       ├── event/
        │                       │   ├── UserCopyEvent.java
        │                       │   └── UserDeleteEvent.java
        │                       ├── manager/
        │                       │   └── UserManager.java
        │                       ├── mapper/
        │                       │   ├── UserMapper.java
        │                       │   └── UserStorageSourceMapper.java
        │                       ├── model/
        │                       │   ├── constant/
        │                       │   │   └── UserConstant.java
        │                       │   ├── dto/
        │                       │   │   └── UserStorageSourceDetailDTO.java
        │                       │   ├── entity/
        │                       │   │   ├── User.java
        │                       │   │   └── UserStorageSource.java
        │                       │   ├── enums/
        │                       │   │   ├── LoginLogModeEnum.java
        │                       │   │   └── LoginVerifyModeEnum.java
        │                       │   ├── request/
        │                       │   │   ├── CheckUserDuplicateRequest.java
        │                       │   │   ├── CopyUserRequest.java
        │                       │   │   ├── QueryUserRequest.java
        │                       │   │   ├── ResetAdminUserNameAndPasswordRequest.java
        │                       │   │   ├── SaveUserRequest.java
        │                       │   │   ├── UpdateUserPwdRequest.java
        │                       │   │   ├── UserLoginRequest.java
        │                       │   │   └── VerifyLoginTwoFactorAuthenticatorRequest.java
        │                       │   ├── response/
        │                       │   │   └── UserDetailResponse.java
        │                       │   └── result/
        │                       │       ├── CheckLoginResult.java
        │                       │       ├── LoginResult.java
        │                       │       ├── LoginTwoFactorAuthenticatorResult.java
        │                       │       └── LoginVerifyImgResult.java
        │                       ├── service/
        │                       │   ├── DynamicLoginEntryService.java
        │                       │   ├── UserService.java
        │                       │   ├── UserStorageSourceService.java
        │                       │   └── login/
        │                       │       ├── ImgVerifyCodeService.java
        │                       │       ├── LoginService.java
        │                       │       ├── TwoFactorAuthenticatorVerifyService.java
        │                       │       └── verify/
        │                       │           ├── LoginVerifyService.java
        │                       │           └── impl/
        │                       │               ├── ImgCodeLoginVerifyService.java
        │                       │               ├── PasswordVerifyService.java
        │                       │               └── TwoFactorAuthLoginVerifyService.java
        │                       ├── util/
        │                       │   └── LoginEntryPathUtils.java
        │                       └── utils/
        │                           └── PasswordVerifyUtils.java
        └── resources/
            ├── META-INF/
            │   └── additional-spring-configuration-metadata.json
            ├── application-default.properties
            ├── application-dev.properties
            ├── application-prod.properties
            ├── application.properties
            ├── banner.txt
            ├── db/
            │   ├── migration-mysql/
            │   │   ├── R__data.sql
            │   │   ├── V10__system_config_add_field_webdav.sql
            │   │   ├── V11__system_config_modify_field_only_office_url_to_https.sql
            │   │   ├── V12__system_config_modify_field_value_to_text.sql
            │   │   ├── V13__system_config_add_field_allow_path_link_anon_access.sql
            │   │   ├── V14__system_config_add_field_load_more_size.sql
            │   │   ├── V15__system_config_add_field_site_home_name.sql
            │   │   ├── V16__system_config_add_field_default_sort_field.sql
            │   │   ├── V17__system_config_add_field_link_limit_field.sql
            │   │   ├── V18__download_log_add_field_download_type.sql
            │   │   ├── V19__short_link_add_field_expire_date.sql
            │   │   ├── V1__Base_version.sql
            │   │   ├── V20__system_config_add_field_favicon_url_field.sql
            │   │   ├── V21__system_config_add_field_expire_times_field.sql
            │   │   ├── V22__system_config_add_field_default_save_pwd_field.sql
            │   │   ├── V23__system_config_add_field_only_office_secret_field.sql
            │   │   ├── V24__system_config_add_field_enable_hover_menu_field.sql
            │   │   ├── V25__system_config_add_field_site_access_field.sql
            │   │   ├── V26__system_config_add_field_login_verify.sql
            │   │   ├── V27__add_table_login_log.sql
            │   │   ├── V28__add_multi_user.sql
            │   │   ├── V29__system_config_add_field_login_verify.sql
            │   │   ├── V2__download_log_modify_storage_key_field_length.sql
            │   │   ├── V30__delete_storage_source_auto_cors_config.sql
            │   │   ├── V31__system_config_add_field_webdav.sql
            │   │   ├── V32__system_config_delete_domain_field.sql
            │   │   ├── V33__storage_source_config_update_field.sql
            │   │   ├── V34__storage_source_config_update_field.sql
            │   │   ├── V35__system_config_add_field_login_log_mode.sql
            │   │   ├── V36__user_add_field_salt.sql
            │   │   ├── V37__set_login_log_model_default_off.sql
            │   │   ├── V38__update_login_log_ip_field_length.sql
            │   │   ├── V3__system_config_add_field_file_click_mode.sql
            │   │   ├── V40__system_config_add_field_mobile_layout.sql
            │   │   ├── V41__system_config_add_custom_office_suffix.sql
            │   │   ├── V42__system_config_add_guest_index_html.sql
            │   │   ├── V43__set_2fa_default_value.sql
            │   │   ├── V44__system_config_add_mobile_.sql
            │   │   ├── V45__add_sso_config.sql
            │   │   ├── V46__add_template_user.sql
            │   │   ├── V47__system_config_add_force_backend_address.sql
            │   │   ├── V48__system_config_add_field_kkfileview_url.sql
            │   │   ├── V49__system_config_add_custom_kkfileview_suffix.sql
            │   │   ├── V4__download_log_modify_ip_field_length.sql
            │   │   ├── V50__system_config_add_kkfileview_open_mode.sql
            │   │   ├── V51__storage_source_config_add_refresh_token_expired_at.sql
            │   │   ├── V52__ststem_config_add_mobile_show_file_size.sql
            │   │   ├── V53__readme_config_add_path_mode_field.sql
            │   │   ├── V54__add_share_link_table.sql
            │   │   ├── V55__system_config_add_secure_login_entry.sql
            │   │   ├── V56__system_config_add_download_confirm_flags.sql
            │   │   ├── V57__user_add_default_share_permissions.sql
            │   │   ├── V5__add_permission_config_table.sql
            │   │   ├── V6__system_config_add_field_auth_code.sql
            │   │   ├── V7__system_config_add_field_max_file_uploads.sql
            │   │   ├── V8__storage_source_add_field_compatibility_readme.sql
            │   │   └── V9__system_config_add_field_only_office_url.sql
            │   └── migration-sqlite/
            │       ├── R__data.sql
            │       ├── V10__system_config_add_field_webdav.sql
            │       ├── V11__system_config_modify_field_only_office_url_to_https.sql
            │       ├── V12__system_config_modify_field_value_to_text.sql
            │       ├── V13__system_config_add_field_allow_path_link_anon_access.sql
            │       ├── V14__system_config_add_field_load_more_size.sql
            │       ├── V15__system_config_add_field_site_home_name.sql
            │       ├── V16__system_config_add_field_default_sort_field.sql
            │       ├── V17__system_config_add_field_link_limit_field.sql
            │       ├── V18__download_log_add_field_download_type.sql
            │       ├── V19__short_link_add_field_expire_date.sql
            │       ├── V1__Base_version.sql
            │       ├── V20__system_config_add_field_favicon_url_field.sql
            │       ├── V21__system_config_add_field_expire_times_field.sql
            │       ├── V22__system_config_add_field_default_save_pwd_field.sql
            │       ├── V23__system_config_add_field_only_office_secret_field.sql
            │       ├── V24__system_config_add_field_enable_hover_menu_field.sql
            │       ├── V25__system_config_add_field_site_access_field.sql
            │       ├── V26__system_config_add_field_login_verify.sql
            │       ├── V27__add_table_login_log.sql
            │       ├── V28__add_multi_user.sql
            │       ├── V29__system_config_add_field_login_verify.sql
            │       ├── V2__download_log_modify_storage_key_field_length.sql
            │       ├── V30__delete_storage_source_auto_cors_config.sql
            │       ├── V31__system_config_add_field_webdav.sql
            │       ├── V32__system_config_delete_domain_field.sql
            │       ├── V33__storage_source_config_update_field.sql
            │       ├── V34__storage_source_config_update_field.sql
            │       ├── V35__system_config_add_field_login_log_mode.sql
            │       ├── V36__user_add_field_salt.sql
            │       ├── V37__fix_user_create_time_field_to_timestamp.sql
            │       ├── V38__set_login_log_model_default_off.sql
            │       ├── V3__system_config_add_field_file_click_mode.sql
            │       ├── V40__system_config_add_field_mobile_layout.sql
            │       ├── V41__system_config_add_custom_office_suffix.sql
            │       ├── V42__system_config_add_guest_index_html.sql
            │       ├── V43__set_2fa_default_value.sql
            │       ├── V44__system_config_add_mobile_.sql
            │       ├── V45__add_sso_config.sql
            │       ├── V46__add_template_user.sql
            │       ├── V47__system_config_add_force_backend_address.sql
            │       ├── V48__system_config_add_field_kkfileview_url.sql
            │       ├── V49__system_config_add_custom_kkfileview_suffix.sql
            │       ├── V4__download_log_modify_ip_field_length.sql
            │       ├── V50__system_config_add_kkfileview_open_mode.sql
            │       ├── V51__storage_source_config_add_refresh_token_expired_at.sql
            │       ├── V52__ststem_config_add_mobile_show_file_size.sql
            │       ├── V53__readme_config_add_path_mode_field.sql
            │       ├── V54__add_share_link_table.sql
            │       ├── V55__system_config_add_secure_login_entry.sql
            │       ├── V56__system_config_add_download_confirm_flags.sql
            │       ├── V57__user_add_default_share_permissions.sql
            │       ├── V5__add_permission_config_table.sql
            │       ├── V6__system_config_add_field_auth_code.sql
            │       ├── V7__system_config_add_field_max_file_uploads.sql
            │       ├── V8__storage_source_add_field_compatibility_readme.sql
            │       └── V9__system_config_add_field_only_office_url.sql
            ├── logback-spring.xml
            ├── mapper/
            │   ├── DownloadLogMapper.xml
            │   ├── FilterConfigMapper.xml
            │   ├── LoginLogMapper.xml
            │   ├── PasswordConfigMapper.xml
            │   ├── PermissionConfigMapper.xml
            │   ├── ReadmeConfigMapper.xml
            │   ├── ShareLinkMapper.xml
            │   ├── ShortLinkMapper.xml
            │   ├── SsoConfigMapper.xml
            │   ├── StorageConfigMapper.xml
            │   ├── StorageSourceMapper.xml
            │   ├── SystemConfigMapper.xml
            │   ├── UserMapper.xml
            │   └── UserStorageSourceMapper.xml
            └── templates/
                ├── callback.html
                └── error/
                    └── 404.html
Download .txt
SYMBOL INDEX (1674 symbols across 418 files)

FILE: src/main/java/im/zhaojun/zfile/ZfileApplication.java
  class ZfileApplication (line 13) | @SpringBootApplication
    method main (line 19) | public static void main(String[] args) {

FILE: src/main/java/im/zhaojun/zfile/core/aspect/ApiLimitAspect.java
  class ApiLimitAspect (line 25) | @Aspect
    method before (line 38) | @Before("@annotation(apiLimit)")

FILE: src/main/java/im/zhaojun/zfile/core/aspect/CommonResultControllerAdvice.java
  class CommonResultControllerAdvice (line 23) | @ControllerAdvice
    method supports (line 26) | @Override
    method beforeBodyWrite (line 32) | @Override
    method getOrCreateContainer (line 50) | private MappingJacksonValue getOrCreateContainer(Object body) {
    method beforeBodyWriteInternal (line 55) | private void beforeBodyWriteInternal(MappingJacksonValue bodyContainer,

FILE: src/main/java/im/zhaojun/zfile/core/aspect/DemoDisableAspect.java
  class DemoDisableAspect (line 19) | @Aspect
    method demoDisable (line 29) | @Pointcut("@annotation(im.zhaojun.zfile.core.annotation.DemoDisable)")
    method before (line 38) | @Before("demoDisable()")

FILE: src/main/java/im/zhaojun/zfile/core/cache/ZFileCacheManager.java
  class ZFileCacheManager (line 14) | @Component
    method findAllEnableOrderByOrderNum (line 33) | public List<StorageSource> findAllEnableOrderByOrderNum(Integer userId...
    method clearUserEnableStorageSourceCache (line 40) | public void clearUserEnableStorageSourceCache() {
    method clearUserEnableStorageSourceCache (line 50) | public void clearUserEnableStorageSourceCache(Integer userId) {

FILE: src/main/java/im/zhaojun/zfile/core/config/ZFileProperties.java
  class ZFileProperties (line 13) | @Data
    class OAuth2Properties (line 30) | @Data
    class Open115Properties (line 38) | @Data

FILE: src/main/java/im/zhaojun/zfile/core/config/datasource/DataSourceBeanPostProcessor.java
  class DataSourceBeanPostProcessor (line 35) | @Slf4j
    method postProcessBeforeInitialization (line 49) | @Override
    method processSqliteDataSource (line 68) | private void processSqliteDataSource(HikariDataSource dataSource) {
    method processFlywayLocations (line 109) | private void processFlywayLocations(FlywayProperties flywayProperties) {
    method getOrder (line 118) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/docs/Knife4jConfiguration.java
  class Knife4jConfiguration (line 19) | @Configuration
    method groupedOpenApi (line 22) | @Bean
    method groupedOpenApi2 (line 32) | @Bean
    method globalOperationCustomizer (line 43) | public OperationCustomizer globalOperationCustomizer() {
    method customOpenAPI (line 54) | @Bean

FILE: src/main/java/im/zhaojun/zfile/core/config/jackson/JSONStringDeserializer.java
  class JSONStringDeserializer (line 16) | public class JSONStringDeserializer extends JsonDeserializer<String> {
    method deserialize (line 18) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/jackson/JSONStringSerializer.java
  class JSONStringSerializer (line 15) | public class JSONStringSerializer extends JsonSerializer<String> {
    method serialize (line 17) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/CollectionIntegerTypeHandler.java
  class CollectionIntegerTypeHandler (line 5) | public class CollectionIntegerTypeHandler extends CollectionTypeHandler<...

FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/CollectionStrTypeHandler.java
  class CollectionStrTypeHandler (line 5) | public class CollectionStrTypeHandler extends CollectionTypeHandler<Set<...

FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/CollectionTypeHandler.java
  class CollectionTypeHandler (line 26) | @MappedJdbcTypes(JdbcType.VARCHAR)
    method setNonNullParameter (line 29) | @Override
    method getNullableResult (line 43) | @Override
    method getNullableResult (line 50) | @Override
    method getNullableResult (line 57) | @Override
    method CollectionTypeHandler (line 71) | public CollectionTypeHandler() {
    method convertToEntityAttribute (line 88) | private Object convertToEntityAttribute(String dbData) {

FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/MyBatisPlusConfig.java
  class MyBatisPlusConfig (line 18) | @Configuration
    method mybatisPlusInterceptor (line 24) | @Bean

FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/MyDatabaseIdProvider.java
  class MyDatabaseIdProvider (line 17) | @Component
    method getDatabaseId (line 23) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/mybatis/MyMetaObjectHandler.java
  class MyMetaObjectHandler (line 16) | @Slf4j
    method insertFill (line 20) | @Override
    method updateFill (line 25) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/security/SaSessionForJacksonCustomized.java
  class SaSessionForJacksonCustomized (line 12) | @JsonIgnoreProperties({"timeout"})
    method SaSessionForJacksonCustomized (line 20) | public SaSessionForJacksonCustomized() {
    method SaSessionForJacksonCustomized (line 28) | public SaSessionForJacksonCustomized(String id) {

FILE: src/main/java/im/zhaojun/zfile/core/config/security/SaTokenConfigure.java
  class SaTokenConfigure (line 16) | @Configuration
    method addInterceptors (line 25) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/security/SaTokenDaoRedisJackson.java
  class SaTokenDaoRedisJackson (line 45) | @Component
    method init (line 83) | @Autowired
    method get (line 151) | @Override
    method set (line 159) | @Override
    method update (line 175) | @Override
    method delete (line 188) | @Override
    method getTimeout (line 196) | @Override
    method updateTimeout (line 204) | @Override
    method getObject (line 225) | @Override
    method setObject (line 233) | @Override
    method updateObject (line 249) | @Override
    method deleteObject (line 262) | @Override
    method getObjectTimeout (line 270) | @Override
    method updateObjectTimeout (line 278) | @Override
    method searchData (line 298) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/security/StpInterfaceImpl.java
  class StpInterfaceImpl (line 17) | @Component
    method getPermissionList (line 30) | @Override
    method getRoleList (line 38) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/spring/JacksonEnumDeserializer.java
  class JacksonEnumDeserializer (line 22) | @Setter
    method deserialize (line 41) | @Override
    method createContextual (line 74) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/spring/SpringCacheConfig.java
  class SpringCacheConfig (line 20) | @Configuration
    method cacheManager (line 30) | @Bean

FILE: src/main/java/im/zhaojun/zfile/core/config/spring/StringToEnumConverterFactory.java
  class StringToEnumConverterFactory (line 25) | @Slf4j
    method getConverter (line 38) | @Override
    class StringToEnumConverter (line 50) | static class StringToEnumConverter<T extends Enum<?>> implements Conve...
      method StringToEnumConverter (line 54) | StringToEnumConverter(Class<T> enumType) {
      method convert (line 69) | @Override
    method getMethod (line 81) | public static <T> Method getMethod(Class<T> enumType) {
    method dealEnumType (line 112) | private static Optional<Field> dealEnumType(Class<?> clazz) {

FILE: src/main/java/im/zhaojun/zfile/core/config/spring/WebMvcConfig.java
  class WebMvcConfig (line 16) | @Configuration
    method webServerFactory (line 22) | @Bean
    method addFormatters (line 38) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/config/totp/TotpAutoConfiguration.java
  class TotpAutoConfiguration (line 17) | @Configuration
    method TotpAutoConfiguration (line 24) | @Autowired
    method secretGenerator (line 29) | @Bean
    method timeProvider (line 36) | @Bean
    method hashingAlgorithm (line 42) | @Bean
    method qrDataFactory (line 48) | @Bean
    method codeGenerator (line 54) | @Bean
    method codeVerifier (line 60) | @Bean
    method getCodeLength (line 69) | private int getCodeLength() {
    method getTimePeriod (line 73) | private int getTimePeriod() {

FILE: src/main/java/im/zhaojun/zfile/core/config/totp/TotpProperties.java
  class TotpProperties (line 5) | @ConfigurationProperties(
    method TotpProperties (line 17) | public TotpProperties() {
    method getSecret (line 20) | public Secret getSecret() {
    method getCode (line 24) | public Code getCode() {
    method getTime (line 28) | public Time getTime() {
    class Time (line 32) | public static class Time {
      method Time (line 36) | public Time() {
      method getPeriod (line 39) | public int getPeriod() {
      method setPeriod (line 43) | public void setPeriod(int period) {
      method getDiscrepancy (line 47) | public int getDiscrepancy() {
      method setDiscrepancy (line 51) | public void setDiscrepancy(int discrepancy) {
    class Code (line 56) | public static class Code {
      method Code (line 59) | public Code() {
      method getLength (line 62) | public int getLength() {
      method setLength (line 66) | public void setLength(int length) {
    class Secret (line 71) | public static class Secret {
      method Secret (line 74) | public Secret() {
      method getLength (line 77) | public int getLength() {
      method setLength (line 81) | public void setLength(int length) {

FILE: src/main/java/im/zhaojun/zfile/core/constant/MdcConstant.java
  class MdcConstant (line 8) | public class MdcConstant {

FILE: src/main/java/im/zhaojun/zfile/core/constant/RuleTypeConstant.java
  class RuleTypeConstant (line 8) | public class RuleTypeConstant {

FILE: src/main/java/im/zhaojun/zfile/core/constant/ZFileConstant.java
  class ZFileConstant (line 12) | @Configuration
    method setTextMaxFileSizeMb (line 20) | @Autowired(required = false)

FILE: src/main/java/im/zhaojun/zfile/core/constant/ZFileHttpHeaderConstant.java
  class ZFileHttpHeaderConstant (line 8) | public class ZFileHttpHeaderConstant {

FILE: src/main/java/im/zhaojun/zfile/core/controller/FrontIndexController.java
  class FrontIndexController (line 23) | @Slf4j
    method redirect (line 39) | @RequestMapping(value = { "/"})
    method guest (line 97) | @RequestMapping(value = { "/guest"})

FILE: src/main/java/im/zhaojun/zfile/core/controller/LogController.java
  class LogController (line 26) | @Tag(name = "日志")
    method downloadLog (line 36) | @GetMapping("/log/download")

FILE: src/main/java/im/zhaojun/zfile/core/exception/ErrorCode.java
  type ErrorCode (line 10) | @Getter
    method ErrorCode (line 121) | ErrorCode(String code, String message) {
    method setCode (line 132) | public ErrorCode setCode(String code) {
    method setMessage (line 143) | public ErrorCode setMessage(String message) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/GlobalExceptionHandler.java
  class GlobalExceptionHandler (line 44) | @ControllerAdvice
    method unauthorizedAccessException (line 62) | @ExceptionHandler(value = UnauthorizedAccessException.class)
    method forbiddenAccessException (line 79) | @ExceptionHandler(value = {
    method forbiddenAccessException (line 98) | @ExceptionHandler(value = ForbiddenAccessException.class)
    method notFoundAccessException (line 115) | @ExceptionHandler(value = NotFoundAccessException.class)
    method notFoundAccessException (line 139) | @ExceptionHandler(value = NoResourceFoundException.class)
    method methodNotAllowedAccessException (line 145) | @ExceptionHandler(value = MethodNotAllowedAccessException.class)
    method badRequestAccessException (line 152) | @ExceptionHandler(value = BadRequestAccessException.class)
    method apiHttpRequestBizException (line 166) | @ExceptionHandler(value = APIHttpRequestBizException.class)
    method filePathSecurityBizException (line 174) | @ExceptionHandler(value = FilePathSecurityBizException.class)
    method getPreviewTextContentBizException (line 182) | @ExceptionHandler(value = GetPreviewTextContentBizException.class)
    method initializeStorageSourceBizException (line 190) | @ExceptionHandler(value = InitializeStorageSourceBizException.class)
    method storageSourceFileForbiddenAccessBizException (line 198) | @ExceptionHandler(value = StorageSourceFileForbiddenAccessBizException...
    method storageSourceIllegalOperationBizException (line 206) | @ExceptionHandler(value = StorageSourceIllegalOperationBizException.cl...
    method corsBizException (line 214) | @ExceptionHandler(value = CorsBizException.class)
    method errorPageBizException (line 222) | @ExceptionHandler(value = ErrorPageBizException.class)
    method bizException (line 240) | @ExceptionHandler(value = BizException.class)
    method uploadFileFailSystemException (line 252) | @ExceptionHandler(value = UploadFileFailSystemException.class)
    method zfileAuthorizationSystemException (line 261) | @ExceptionHandler(value = ZFileAuthorizationSystemException.class)
    method systemException (line 268) | @ExceptionHandler(value = SystemException.class)
    method handleValidException (line 283) | @ExceptionHandler(value = {MethodArgumentNotValidException.class, Bind...
    method fileNotFound (line 306) | @ExceptionHandler({FileNotFoundException.class})
    method handlerNotLoginException (line 317) | @ExceptionHandler(NotLoginException.class)
    method extraExceptionHandler (line 338) | @ExceptionHandler
    method getExceptionType (line 367) | private static ExceptionType getExceptionType(Exception e) {
    type ExceptionType (line 385) | enum ExceptionType {

FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/APIHttpRequestBizException.java
  class APIHttpRequestBizException (line 15) | @Getter
    method APIHttpRequestBizException (line 24) | public APIHttpRequestBizException(ErrorCode errorCode, String url, int...

FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/CorsBizException.java
  class CorsBizException (line 9) | @Getter
    method CorsBizException (line 12) | public CorsBizException(String message, Throwable cause) {
    method printExceptionStackTrace (line 16) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/FilePathSecurityBizException.java
  class FilePathSecurityBizException (line 16) | @Getter
    method FilePathSecurityBizException (line 21) | public FilePathSecurityBizException(String path) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/GetPreviewTextContentBizException.java
  class GetPreviewTextContentBizException (line 14) | @Getter
    method GetPreviewTextContentBizException (line 22) | public GetPreviewTextContentBizException(String url, Throwable cause) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/InitializeStorageSourceBizException.java
  class InitializeStorageSourceBizException (line 14) | @Getter
    method InitializeStorageSourceBizException (line 19) | public InitializeStorageSourceBizException(String message, Integer sto...
    method InitializeStorageSourceBizException (line 24) | public InitializeStorageSourceBizException(String code, String message...

FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/InvalidStorageSourceBizException.java
  class InvalidStorageSourceBizException (line 12) | @Getter
    method InvalidStorageSourceBizException (line 19) | public InvalidStorageSourceBizException(String storageKey) {
    method InvalidStorageSourceBizException (line 25) | public InvalidStorageSourceBizException(Integer storageId) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/StorageSourceFileForbiddenAccessBizException.java
  class StorageSourceFileForbiddenAccessBizException (line 15) | @Getter
    method StorageSourceFileForbiddenAccessBizException (line 22) | public StorageSourceFileForbiddenAccessBizException(Integer storageId,...

FILE: src/main/java/im/zhaojun/zfile/core/exception/biz/StorageSourceIllegalOperationBizException.java
  class StorageSourceIllegalOperationBizException (line 16) | @Getter
    method StorageSourceIllegalOperationBizException (line 23) | public StorageSourceIllegalOperationBizException(Integer storageId, Fi...

FILE: src/main/java/im/zhaojun/zfile/core/exception/core/BizException.java
  class BizException (line 11) | @Getter
    method printExceptionStackTrace (line 24) | public boolean printExceptionStackTrace() {
    method BizException (line 31) | public BizException() {
    method BizException (line 40) | public BizException(Throwable cause) {
    method BizException (line 49) | public BizException(String message) {
    method BizException (line 59) | public BizException(String code, String message) {
    method BizException (line 70) | public BizException(String message, Throwable cause) {
    method BizException (line 79) | public BizException(String code, String message, Throwable cause) {
    method BizException (line 87) | public BizException(ErrorCode errorCode) {
    method BizException (line 96) | public BizException(ErrorCode errorCode, Throwable cause) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/core/ErrorPageBizException.java
  class ErrorPageBizException (line 13) | @Getter
    method printExceptionStackTrace (line 26) | public boolean printExceptionStackTrace() {
    method ErrorPageBizException (line 33) | public ErrorPageBizException() {
    method ErrorPageBizException (line 42) | public ErrorPageBizException(Throwable cause) {
    method ErrorPageBizException (line 51) | public ErrorPageBizException(String message) {
    method ErrorPageBizException (line 61) | public ErrorPageBizException(String code, String message) {
    method ErrorPageBizException (line 72) | public ErrorPageBizException(String message, Throwable cause) {
    method ErrorPageBizException (line 81) | public ErrorPageBizException(String code, String message, Throwable ca...
    method ErrorPageBizException (line 89) | public ErrorPageBizException(ErrorCode errorCode) {
    method ErrorPageBizException (line 98) | public ErrorPageBizException(ErrorCode errorCode, Throwable cause) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/core/SystemException.java
  class SystemException (line 11) | @Getter
    method SystemException (line 24) | public SystemException() {
    method SystemException (line 33) | public SystemException(Throwable cause) {
    method SystemException (line 42) | public SystemException(String message) {
    method SystemException (line 52) | public SystemException(String code, String message) {
    method SystemException (line 63) | public SystemException(String message, Throwable cause) {
    method SystemException (line 72) | public SystemException(String code, String message, Throwable cause) {
    method SystemException (line 80) | public SystemException(ErrorCode errorCode) {
    method SystemException (line 89) | public SystemException(ErrorCode errorCode, Throwable cause) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/status/BadRequestAccessException.java
  class BadRequestAccessException (line 13) | public class BadRequestAccessException extends BizException {
    method BadRequestAccessException (line 15) | public BadRequestAccessException(String message) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/status/ForbiddenAccessException.java
  class ForbiddenAccessException (line 13) | public class ForbiddenAccessException extends BizException {
    method ForbiddenAccessException (line 15) | public ForbiddenAccessException(ErrorCode errorCode) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/status/MethodNotAllowedAccessException.java
  class MethodNotAllowedAccessException (line 14) | public class MethodNotAllowedAccessException extends BizException {
    method MethodNotAllowedAccessException (line 16) | public MethodNotAllowedAccessException(ErrorCode errorCode) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/status/NotFoundAccessException.java
  class NotFoundAccessException (line 13) | public class NotFoundAccessException extends BizException {
    method NotFoundAccessException (line 15) | public NotFoundAccessException(ErrorCode errorCode) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/status/UnauthorizedAccessException.java
  class UnauthorizedAccessException (line 12) | public class UnauthorizedAccessException extends BizException {
    method UnauthorizedAccessException (line 14) | public UnauthorizedAccessException(String message) {

FILE: src/main/java/im/zhaojun/zfile/core/exception/system/UploadFileFailSystemException.java
  class UploadFileFailSystemException (line 17) | @Getter
    method UploadFileFailSystemException (line 30) | public UploadFileFailSystemException(StorageTypeEnum storageTypeEnum, ...
    method UploadFileFailSystemException (line 34) | public UploadFileFailSystemException(StorageTypeEnum storageTypeEnum, ...

FILE: src/main/java/im/zhaojun/zfile/core/exception/system/ZFileAuthorizationSystemException.java
  class ZFileAuthorizationSystemException (line 14) | public class ZFileAuthorizationSystemException extends SystemException {
    method ZFileAuthorizationSystemException (line 16) | public ZFileAuthorizationSystemException(String code, String message) {
    method ZFileAuthorizationSystemException (line 20) | public ZFileAuthorizationSystemException(ErrorCode errorCode) {
    method ZFileAuthorizationSystemException (line 24) | public ZFileAuthorizationSystemException(ErrorCode errorCode, Throwabl...

FILE: src/main/java/im/zhaojun/zfile/core/filter/CorsFilter.java
  class CorsFilter (line 24) | @WebFilter(urlPatterns = "/*")
    method doFilter (line 29) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/filter/MDCFilter.java
  class MDCFilter (line 20) | @WebFilter(urlPatterns = "/*")
    method doFilter (line 23) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/filter/SecurityFilter.java
  class SecurityFilter (line 26) | @WebFilter(urlPatterns = "/*")
    method doFilter (line 31) | @Override
    method checkIsDisableIP (line 68) | private boolean checkIsDisableIP(String accessIpBlocklist, String curr...
    method checkIsDisableUA (line 74) | private boolean checkIsDisableUA(String accessUaBlocklist, String curr...

FILE: src/main/java/im/zhaojun/zfile/core/io/EnsureContentLengthInputStreamResource.java
  class EnsureContentLengthInputStreamResource (line 48) | public class EnsureContentLengthInputStreamResource extends InputStreamR...
    method EnsureContentLengthInputStreamResource (line 56) | public EnsureContentLengthInputStreamResource(InputStream inputStream,...
    method contentLength (line 61) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/model/request/PageQueryRequest.java
  class PageQueryRequest (line 14) | @Data
    method getOrderItem (line 29) | public OrderItem getOrderItem() {

FILE: src/main/java/im/zhaojun/zfile/core/util/AjaxJson.java
  class AjaxJson (line 15) | @Data
    method AjaxJson (line 38) | public AjaxJson(String code, String msg) {
    method AjaxJson (line 47) | public AjaxJson(String code, String msg, T data) {
    method AjaxJson (line 54) | public AjaxJson(String code, String msg, T data, Long dataCount) {
    method getSuccess (line 62) | public static AjaxJson<Void> getSuccess() {
    method getSuccess (line 66) | public static AjaxJson<Void> getSuccess(String msg) {
    method getSuccess (line 70) | public static <T> AjaxJson<T> getSuccess(String msg, T data) {
    method getSuccessData (line 74) | public static <T> AjaxJson<T> getSuccessData(T data) {
    method getPageData (line 79) | public static <T> AjaxJson<T> getPageData(Long dataCount, T data) {
    method getError (line 84) | public static AjaxJson<Void> getError(String msg) {
    method getUnauthorizedResult (line 89) | public static AjaxJson<?> getUnauthorizedResult() {
    method getForbiddenResult (line 94) | public static AjaxJson<?> getForbiddenResult() {
    method getNotFoundResult (line 99) | public static AjaxJson<?> getNotFoundResult() {
    method getError (line 103) | public static AjaxJson<?> getError(String code, String msg) {

FILE: src/main/java/im/zhaojun/zfile/core/util/ArrayUtils.java
  class ArrayUtils (line 8) | public class ArrayUtils {
    method isEmpty (line 21) | public static <T> boolean isEmpty(T[] array) {
    method isNotEmpty (line 36) | public static <T> boolean isNotEmpty(T[] array) {

FILE: src/main/java/im/zhaojun/zfile/core/util/CharPool.java
  type CharPool (line 3) | public interface CharPool {

FILE: src/main/java/im/zhaojun/zfile/core/util/CharSequenceUtil.java
  class CharSequenceUtil (line 15) | public class CharSequenceUtil implements CharPool {
    method length (line 47) | public static int length(final @Nullable CharSequence ch) {
    method str (line 60) | public static String str(final @Nullable CharSequence cs) {
    method isEmpty (line 73) | public static boolean isEmpty(final @Nullable CharSequence cs) {
    method isNotEmpty (line 86) | public static boolean isNotEmpty(final @Nullable CharSequence cs) {
    method isAllEmpty (line 110) | public static boolean isAllEmpty(final @Nullable CharSequence... strs) {
    method hasEmpty (line 142) | public static boolean hasEmpty(final @Nullable CharSequence... strs) {
    method isAllNotEmpty (line 175) | public static boolean isAllNotEmpty(final @Nullable CharSequence... ar...
    method isBlank (line 188) | public static boolean isBlank(final @Nullable CharSequence ch) {
    method isNotBlank (line 210) | public static boolean isNotBlank(final @Nullable CharSequence cs) {
    method equals (line 226) | public static boolean equals(final @Nullable CharSequence cs1, final @...
    method equals (line 264) | public static boolean equals(final @Nullable CharSequence cs1,final @N...
    method equalsIgnoreCase (line 280) | public static boolean equalsIgnoreCase(final @Nullable CharSequence cs...
    method split (line 306) | public static List<String> split(final CharSequence str, final CharSeq...
    method split (line 328) | public static List<String> split(CharSequence str, CharSequence separa...
    method split (line 353) | public static List<String> split(CharSequence str, CharSequence separa...
    method contains (line 370) | public static boolean contains(final @Nullable CharSequence str, final...
    method containsAny (line 389) | public static boolean containsAny(final @Nullable CharSequence str, fi...
    method containsAnyIgnoreCase (line 414) | public static boolean containsAnyIgnoreCase(final @Nullable CharSequen...
    method join (line 430) | public static String join(CharSequence conjunction, Object... objs) {
    method join (line 454) | public static String join(CharSequence conjunction, Collection<?> coll...
    method startWith (line 477) | public static boolean startWith(CharSequence str, CharSequence prefix) {
    method startWithIgnoreCase (line 493) | public static boolean startWithIgnoreCase(CharSequence str, CharSequen...
    method startWith (line 513) | public static boolean startWith(CharSequence str, CharSequence prefix,...
    method startWith (line 541) | public static boolean startWith(final @Nullable CharSequence str, fina...
    method endWith (line 570) | public static boolean endWith(final @Nullable CharSequence str, final ...
    method endWith (line 590) | public static boolean endWith(final @Nullable CharSequence str, final ...
    method endWith (line 613) | public static boolean endWith(final @Nullable CharSequence str, final ...
    method removePrefix (line 643) | public static String removePrefix(final @Nullable CharSequence str, fi...
    method firstNonNull (line 667) | @SuppressWarnings("unchecked")
    method subBefore (line 707) | public static String subBefore(final @Nullable CharSequence string, fi...

FILE: src/main/java/im/zhaojun/zfile/core/util/ClassUtils.java
  class ClassUtils (line 12) | public class ClassUtils {
    method forName (line 14) | public static Class<?> forName(String className) {
    method getClassFirstGenericsParam (line 30) | public static Class<?> getClassFirstGenericsParam(Class<?> clazz) {
    method getGenericType (line 36) | public static Class<?> getGenericType(Field field) {

FILE: src/main/java/im/zhaojun/zfile/core/util/CollectionUtils.java
  class CollectionUtils (line 8) | public class CollectionUtils {
    method isEmpty (line 19) | public static boolean isEmpty(@Nullable Collection<?> collection) {
    method isNotEmpty (line 32) | public static boolean isNotEmpty(@Nullable Collection<?> collection) {
    method getFirst (line 45) | @Nullable
    method getLast (line 62) | @Nullable
    method addAll (line 85) | public static <T> Collection<T> addAll(Collection<T> collection, T[] v...
    method toMap (line 114) | public static <K, V> Map<K, V> toMap(final @Nullable Iterable<V> value...

FILE: src/main/java/im/zhaojun/zfile/core/util/DnsUtil.java
  class DnsUtil (line 7) | public class DnsUtil {
    method getDomainIpByHttpDns (line 17) | public static @Nullable String[] getDomainIpByHttpDns(String domain) {
    method getDomainIpByHttpDnsAndCache (line 43) | public static String[] getDomainIpByHttpDnsAndCache(String domain, int...

FILE: src/main/java/im/zhaojun/zfile/core/util/EnumConvertUtils.java
  class EnumConvertUtils (line 17) | public class EnumConvertUtils {
    method convertStrToEnum (line 31) | public static Enum<?> convertStrToEnum(Class<?> clazz, Object value) {
    method convertEnumToStr (line 63) | public static String convertEnumToStr(Object enumObj) {

FILE: src/main/java/im/zhaojun/zfile/core/util/FileComparator.java
  class FileComparator (line 19) | public class FileComparator implements Comparator<FileItemResult> {
    method FileComparator (line 25) | public FileComparator(String sortBy, String order) {
    method compare (line 42) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/util/FileResponseUtil.java
  class FileResponseUtil (line 22) | @Slf4j
    method exportSingleThread (line 37) | public static ResponseEntity<Resource> exportSingleThread(File file, S...

FILE: src/main/java/im/zhaojun/zfile/core/util/FileSizeConverter.java
  class FileSizeConverter (line 6) | public class FileSizeConverter {
    method convertFileSizeToBytes (line 16) | public static long convertFileSizeToBytes(String sizeStr) {

FILE: src/main/java/im/zhaojun/zfile/core/util/FileUtils.java
  class FileUtils (line 10) | public class FileUtils {
    method getName (line 12) | public static String getName(final String fileName) {
    method getParentPath (line 25) | public static String getParentPath(final String fileName) {
    method getExtension (line 33) | public static String getExtension(final String fileName) throws Illega...

FILE: src/main/java/im/zhaojun/zfile/core/util/HttpUtil.java
  class HttpUtil (line 18) | @Slf4j
    method getTextContent (line 29) | public static String getTextContent(String url) {
    method getRemoteFileSize (line 55) | public static Long getRemoteFileSize(String url) {

FILE: src/main/java/im/zhaojun/zfile/core/util/NaturalOrderComparator.java
  class NaturalOrderComparator (line 33) | public class NaturalOrderComparator implements Comparator<String> {
    method compareRight (line 37) | private int compareRight(String a, String b) {
    method compare (line 71) | @Override
    method isDigit (line 132) | private static boolean isDigit(char c) {
    method charAt (line 136) | private static char charAt(String s, int i) {
    method compareEqual (line 140) | private static int compareEqual(String a, String b, int nza, int nzb) {

FILE: src/main/java/im/zhaojun/zfile/core/util/NumberUtils.java
  class NumberUtils (line 8) | public class NumberUtils {
    method isNullOrZero (line 10) | public static boolean isNullOrZero(Integer number) {
    method isNotNullOrZero (line 14) | public static boolean isNotNullOrZero(Integer number) {

FILE: src/main/java/im/zhaojun/zfile/core/util/OnlyOfficeKeyCacheUtils.java
  class OnlyOfficeKeyCacheUtils (line 22) | @Slf4j
    method getKeyOrPutNew (line 48) | public static String getKeyOrPutNew(OnlyOfficeFile onlyOfficeFile, lon...
    method removeByKey (line 80) | public static OnlyOfficeFile removeByKey(String key) {
    method removeByFile (line 96) | public static OnlyOfficeFile removeByFile(OnlyOfficeFile onlyOfficeFil...
    method removeByFolder (line 113) | public static List<OnlyOfficeFile> removeByFolder(OnlyOfficeFile onlyO...
    method getLock (line 137) | public static ReentrantLock getLock(OnlyOfficeFile key) {

FILE: src/main/java/im/zhaojun/zfile/core/util/PatternMatcherUtils.java
  class PatternMatcherUtils (line 14) | public class PatternMatcherUtils {
    method testCompatibilityGlobPattern (line 37) | public static boolean testCompatibilityGlobPattern(String pattern, Str...
    method testGlobPattern (line 61) | private static boolean testGlobPattern(String pattern, String test) {

FILE: src/main/java/im/zhaojun/zfile/core/util/PlaceholderUtils.java
  class PlaceholderUtils (line 13) | @Slf4j
    method resolvePlaceholdersBySpringProperties (line 38) | public static String resolvePlaceholdersBySpringProperties(String form...
    method resolvePlaceholders (line 62) | public static String resolvePlaceholders(String formatStr, Map<String,...
    method getFirstPlaceholderName (line 101) | public static String getFirstPlaceholderName(String formatStr) {
    method getPlaceholderNames (line 118) | public static List<String> getPlaceholderNames(String formatStr) {

FILE: src/main/java/im/zhaojun/zfile/core/util/ProxyDownloadUrlUtils.java
  class ProxyDownloadUrlUtils (line 21) | @Slf4j
    method generatorSignature (line 49) | public static String generatorSignature(Integer storageId, String path...
    method validSignatureExpired (line 71) | public static boolean validSignatureExpired(Integer expectedStorageId,...

FILE: src/main/java/im/zhaojun/zfile/core/util/RequestHolder.java
  class RequestHolder (line 34) | @Slf4j
    method getRequest (line 42) | public static HttpServletRequest getRequest() {
    method getResponse (line 52) | public static HttpServletResponse getResponse() {
    method writeFile (line 77) | public static void writeFile(InputStream inputStream, String fileName,...
    method isAxiosRequest (line 140) | public static boolean isAxiosRequest() {
    method getAxiosFrom (line 152) | public static String getAxiosFrom() {
    method getRequestServerAddress (line 163) | public static String getRequestServerAddress() {
    method getOriginAddress (line 222) | public static String getOriginAddress() {

FILE: src/main/java/im/zhaojun/zfile/core/util/RequestUtils.java
  class RequestUtils (line 8) | public class RequestUtils {
    method getRequestRange (line 10) | public static HttpRange getRequestRange(HttpServletRequest request) {

FILE: src/main/java/im/zhaojun/zfile/core/util/SizeToStrUtils.java
  class SizeToStrUtils (line 10) | public class SizeToStrUtils {
    method bytesToSize (line 20) | public static String bytesToSize(long bytes) {
    method bpsToSize (line 40) | public static String bpsToSize(long bps) {

FILE: src/main/java/im/zhaojun/zfile/core/util/SpringMvcUtils.java
  class SpringMvcUtils (line 11) | public class SpringMvcUtils {
    method getExtractPathWithinPattern (line 13) | public static String getExtractPathWithinPattern() {

FILE: src/main/java/im/zhaojun/zfile/core/util/StrPool.java
  type StrPool (line 3) | public interface StrPool {

FILE: src/main/java/im/zhaojun/zfile/core/util/StringUtils.java
  class StringUtils (line 18) | public class StringUtils extends CharSequenceUtil implements StrPool {
    method trimSlashes (line 35) | public static String trimSlashes(String path) {
    method trimStartSlashes (line 52) | public static String trimStartSlashes(String path) {
    method trimEndSlashes (line 74) | public static String trimEndSlashes(String path) {
    method removeDuplicateSlashes (line 96) | public static String removeDuplicateSlashes(String path) {
    method removeDuplicateSlashesAndTrimStart (line 150) | public static String removeDuplicateSlashesAndTrimStart(String path) {
    method removeDuplicateSlashesAndTrimEnd (line 166) | public static String removeDuplicateSlashesAndTrimEnd(String path) {
    method concatTrimStartSlashes (line 181) | public static String concatTrimStartSlashes(String... strs) {
    method concatTrimEndSlashes (line 194) | public static String concatTrimEndSlashes(String... strs) {
    method concatTrimSlashes (line 207) | public static String concatTrimSlashes(String... strs) {
    method concat (line 220) | public static String concat(String... strs) {
    method concat (line 247) | public static String concat(boolean encodeAllIgnoreSlashes, String... ...
    method replaceHost (line 268) | public static String replaceHost(String originUrl, String replaceHost) {
    method encode (line 298) | public static String encode(String url) {
    method encodeAllIgnoreSlashes (line 311) | public static String encodeAllIgnoreSlashes(String str) {
    method decode (line 348) | public static String decode(String url) {
    method removeAllLineBreaksAndTrim (line 361) | public static String removeAllLineBreaksAndTrim(String str) {
    method trim (line 375) | public static String trim(final String str) {
    method addSuffixIfNot (line 387) | public static String addSuffixIfNot(CharSequence str, CharSequence suf...
    method containsIgnoreCase (line 406) | public static boolean containsIgnoreCase(CharSequence str, CharSequenc...
    method indexOf (line 426) | public static int indexOf(String str, char searchChar) {
    method camelToUnderline (line 442) | public static String camelToUnderline(String param) {
    method setSchema (line 471) | public static String setSchema(String url, String schema) {

FILE: src/main/java/im/zhaojun/zfile/core/util/UrlUtils.java
  class UrlUtils (line 10) | public class UrlUtils {
    method hasScheme (line 20) | public static boolean hasScheme(String url) {
    method concatQueryParam (line 38) | public static String concatQueryParam(String url, String name, String ...
    method getSchema (line 54) | public static String getSchema(String url) {
    method removeScheme (line 72) | public static String removeScheme(String url) {
    method getDomain (line 90) | public static String getDomain(String url) {

FILE: src/main/java/im/zhaojun/zfile/core/util/ZFileAuthUtil.java
  class ZFileAuthUtil (line 15) | public class ZFileAuthUtil {
    method getCurrentUser (line 19) | public static User getCurrentUser() {
    method getCurrentUserId (line 33) | public static Integer getCurrentUserId() {

FILE: src/main/java/im/zhaojun/zfile/core/util/matcher/AbstractRuleMatcher.java
  class AbstractRuleMatcher (line 10) | public abstract class AbstractRuleMatcher implements IRuleMatcher {
    method contains (line 12) | @Override
    method matchAny (line 17) | @Override
    method matchAnyReturnFirst (line 30) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/util/matcher/IRuleMatcher.java
  type IRuleMatcher (line 10) | public interface IRuleMatcher {
    method match (line 23) | boolean match(String ruleExpression, String testStr);
    method contains (line 36) | boolean contains(String ruleExpression, String testStr);
    method matchAny (line 50) | boolean matchAny(Collection<String> ruleExpressionList, String testStr);
    method matchAnyReturnFirst (line 64) | String matchAnyReturnFirst(Collection<String> ruleExpressionList, Stri...
    method getRuleType (line 72) | String getRuleType();

FILE: src/main/java/im/zhaojun/zfile/core/util/matcher/RuleMatcherFactory.java
  class RuleMatcherFactory (line 16) | public class RuleMatcherFactory {
    method getRuleMatcher (line 34) | public static IRuleMatcher getRuleMatcher(String ruleType) {

FILE: src/main/java/im/zhaojun/zfile/core/util/matcher/impl/AntPathRuleMatcher.java
  class AntPathRuleMatcher (line 13) | @Slf4j
    method match (line 18) | @Override
    method getRuleType (line 27) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/util/matcher/impl/IpRuleMatcher.java
  class IpRuleMatcher (line 38) | @Slf4j
    method match (line 41) | @Override
    method getRuleType (line 57) | @Override
    method createRule (line 62) | private IpRule createRule(String ruleExpression) {
    method isValidIpv4 (line 76) | private boolean isValidIpv4(String ipAddress) {
    method isValidIpv6 (line 84) | private boolean isValidIpv6(String ipAddress) {
    method isValidIpv4Range (line 93) | private boolean isValidIpv4Range(String ipRange) {
    method isValidIpv6Range (line 98) | private boolean isValidIpv6Range(String ipRange) {
    type IpRule (line 103) | private interface IpRule {
      method matches (line 105) | boolean matches(InetAddress ipAddress);
      method getExpression (line 107) | String getExpression();
    class Ipv4Rule (line 111) | private static class Ipv4Rule implements IpRule {
      method Ipv4Rule (line 115) | Ipv4Rule(String expression) {
      method matches (line 119) | @Override
      method getExpression (line 127) | @Override
    class Ipv6Rule (line 133) | private static class Ipv6Rule implements IpRule {
      method Ipv6Rule (line 137) | Ipv6Rule(String expression) {
      method matches (line 141) | @Override
      method getExpression (line 149) | @Override
    class Ipv4RangeRule (line 155) | private static class Ipv4RangeRule implements IpRule {
      method Ipv4RangeRule (line 160) | Ipv4RangeRule(String expression) {
      method matches (line 165) | @Override
      method getExpression (line 201) | @Override
    class Ipv6RangeRule (line 207) | private static class Ipv6RangeRule implements IpRule {
      method Ipv6RangeRule (line 212) | Ipv6RangeRule(String expression) {
      method matches (line 217) | @Override
      method getExpression (line 249) | @Override
    class Inet6AddressConverter (line 256) | private static class Inet6AddressConverter {
      method convert (line 257) | public static byte[] convert(String ipv6Address) {

FILE: src/main/java/im/zhaojun/zfile/core/util/matcher/impl/RegexRuleMatcher.java
  class RegexRuleMatcher (line 13) | @Slf4j
    method match (line 16) | @Override
    method contains (line 25) | @Override
    method getRuleType (line 34) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/util/matcher/impl/SpringSimpleRuleMatcher.java
  class SpringSimpleRuleMatcher (line 13) | @Slf4j
    method match (line 16) | @Override
    method getRuleType (line 25) | @Override
    method contains (line 30) | @Override

FILE: src/main/java/im/zhaojun/zfile/core/validation/StringListValueConstraintValidator.java
  class StringListValueConstraintValidator (line 16) | public class StringListValueConstraintValidator implements ConstraintVal...
    method initialize (line 26) | @Override
    method isValid (line 45) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/admin/controller/IpHelperController.java
  class IpHelperController (line 23) | @Tag(name = "IP 地址辅助 Controller")
    method clientIp (line 32) | @GetMapping("clientIp")
    method serverAddress (line 39) | @GetMapping("serverAddress")
    method headers (line 45) | @GetMapping("headers")

FILE: src/main/java/im/zhaojun/zfile/module/admin/controller/RuleMatcherTestController.java
  class RuleMatcherTestController (line 21) | @Tag(name = "规则匹配辅助 Controller")
    method testRule (line 34) | @PostMapping("/rule-test")

FILE: src/main/java/im/zhaojun/zfile/module/admin/model/request/TestRuleMatcherRequest.java
  class TestRuleMatcherRequest (line 7) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/constant/SystemConfigConstant.java
  class SystemConfigConstant (line 8) | public class SystemConfigConstant {

FILE: src/main/java/im/zhaojun/zfile/module/config/controller/SettingController.java
  class SettingController (line 27) | @Tag(name = "站点设置模块")
    method getConfig (line 39) | @ApiOperationSupport(order = 1)
    method updateSiteSetting (line 53) | @ApiOperationSupport(order = 3)
    method updateViewSetting (line 68) | @ApiOperationSupport(order = 4)
    method updateSecuritySetting (line 80) | @ApiOperationSupport(order = 5)
    method updateLinkSetting (line 95) | @ApiOperationSupport(order = 6)
    method updateSecuritySetting (line 106) | @ApiOperationSupport(order = 7)

FILE: src/main/java/im/zhaojun/zfile/module/config/controller/SiteController.java
  class SiteController (line 34) | @Tag(name = "站点基础模块")
    method globalConfig (line 50) | @ApiOperationSupport(order = 1)
    method storageList (line 68) | @ApiOperationSupport(order = 2)
    method getUserRootPath (line 77) | @ApiOperationSupport(order = 3)

FILE: src/main/java/im/zhaojun/zfile/module/config/event/DirectLinkPrefixModifyHandler.java
  class DirectLinkPrefixModifyHandler (line 17) | @Slf4j
    method modify (line 24) | @Override
    method matches (line 38) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/config/event/ISystemConfigModifyHandler.java
  type ISystemConfigModifyHandler (line 10) | public interface ISystemConfigModifyHandler {
    method modify (line 16) | void modify(SystemConfig originalSystemConfig, SystemConfig newSystemC...
    method matches (line 26) | boolean matches(String name);

FILE: src/main/java/im/zhaojun/zfile/module/config/event/SecureLoginEntryModifyHandler.java
  class SecureLoginEntryModifyHandler (line 17) | @Slf4j
    method modify (line 24) | @Override
    method matches (line 39) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/config/event/SystemConfigModifyHandlerChain.java
  class SystemConfigModifyHandlerChain (line 12) | @Component
    method execute (line 18) | public void execute(SystemConfig originalSystemConfig, SystemConfig ne...

FILE: src/main/java/im/zhaojun/zfile/module/config/mapper/SystemConfigMapper.java
  type SystemConfigMapper (line 16) | @Mapper
    method findAll (line 24) | List<SystemConfig> findAll();
    method findByName (line 35) | SystemConfig findByName(@Param("name")String name);
    method saveAll (line 46) | int saveAll(@Param("list")List<SystemConfig> list);

FILE: src/main/java/im/zhaojun/zfile/module/config/model/dto/LinkExpireDTO.java
  class LinkExpireDTO (line 7) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/model/dto/SystemConfigDTO.java
  class SystemConfigDTO (line 21) | @Data
    method getAnnouncement (line 266) | public String getAnnouncement() {
    method getLinkExpireTimes (line 270) | public List<LinkExpireDTO> getLinkExpireTimes() {
    method getLayout (line 282) | public String getLayout() {
    method getMobileLayout (line 286) | public String getMobileLayout() {
    method getEnableNormalDownloadConfirm (line 295) | public Boolean getEnableNormalDownloadConfirm() {
    method getEnablePackageDownloadConfirm (line 304) | public Boolean getEnablePackageDownloadConfirm() {
    method getEnableBatchDownloadConfirm (line 313) | public Boolean getEnableBatchDownloadConfirm() {

FILE: src/main/java/im/zhaojun/zfile/module/config/model/entity/SystemConfig.java
  class SystemConfig (line 17) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/model/enums/FileClickModeEnum.java
  type FileClickModeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateAccessSettingRequest.java
  class UpdateAccessSettingRequest (line 11) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateLinkSettingRequest.java
  class UpdateLinkSettingRequest (line 16) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateSecuritySettingRequest.java
  class UpdateSecuritySettingRequest (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateSiteSettingRequest.java
  class UpdateSiteSettingRequest (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateUserNameAndPasswordRequest.java
  class UpdateUserNameAndPasswordRequest (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateViewSettingRequest.java
  class UpdateViewSettingRequest (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/model/result/FrontSiteConfigResult.java
  class FrontSiteConfigResult (line 16) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/config/service/SystemConfigService.java
  class SystemConfigService (line 43) | @Slf4j
    method getSystemConfig (line 70) | @Cacheable(key = "'dto'")
    method updateSystemConfig (line 120) | @Transactional(rollbackFor = Exception.class)
    method getAesHexKeyOrGenerate (line 186) | public synchronized String getAesHexKeyOrGenerate() {
    method getFrontDomain (line 210) | public String getFrontDomain() {
    method getRealFrontDomain (line 221) | public String getRealFrontDomain() {
    method getAxiosFromDomainOrSetting (line 235) | public String getAxiosFromDomainOrSetting() {
    method getUnauthorizedUrl (line 252) | public String getUnauthorizedUrl() {
    method getUnauthorizedUrl (line 267) | public String getUnauthorizedUrl(String code, String message) {
    method getForbiddenUrl (line 285) | public String getForbiddenUrl() {
    method getForbiddenUrl (line 300) | public String getForbiddenUrl(String code, String message) {
    method getNotFoundUrl (line 318) | public String getNotFoundUrl() {
    method getNotFoundUrl (line 333) | public String getNotFoundUrl(String code, String message) {
    method getErrorPageUrl (line 356) | public String getErrorPageUrl(String code, String message) {
    method resetLoginVerifyMode (line 372) | public void resetLoginVerifyMode() {

FILE: src/main/java/im/zhaojun/zfile/module/filter/controller/StorageSourceFilterController.java
  class StorageSourceFilterController (line 24) | @Tag(name = "存储源模块-过滤文件")
    method getFilters (line 33) | @ApiOperationSupport(order = 1)
    method saveFilters (line 42) | @ApiOperationSupport(order = 2)

FILE: src/main/java/im/zhaojun/zfile/module/filter/mapper/FilterConfigMapper.java
  type FilterConfigMapper (line 15) | @Mapper
    method findByStorageId (line 26) | List<FilterConfig> findByStorageId(@Param("storageId") Integer storage...
    method deleteByStorageId (line 37) | int deleteByStorageId(@Param("storageId") Integer storageId);
    method findByStorageIdAndInaccessible (line 48) | List<FilterConfig> findByStorageIdAndInaccessible(@Param("storageId")I...
    method findByStorageIdAndDisableDownload (line 59) | List<FilterConfig> findByStorageIdAndDisableDownload(@Param("storageId...

FILE: src/main/java/im/zhaojun/zfile/module/filter/model/entity/FilterConfig.java
  class FilterConfig (line 19) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/filter/model/enums/FilterConfigHiddenModeEnum.java
  type FilterConfigHiddenModeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/filter/service/FilterConfigService.java
  class FilterConfigService (line 32) | @Slf4j
    method findByStorageId (line 51) | @Cacheable(key = "'filter-base-' + #storageId",
    method findByStorageIdAndInaccessible (line 66) | @Cacheable(key = "'filter-inaccessible-' + #storageId",
    method findByStorageIdAndDisableDownload (line 81) | @Cacheable(key = "'filter-disable-download-' + #storageId",
    method batchSave (line 97) | @Transactional(rollbackFor = Exception.class)
    method deleteByStorageId (line 122) | @Caching(evict = {
    method onStorageSourceDelete (line 139) | @EventListener
    method checkFileIsInaccessible (line 163) | public boolean checkFileIsInaccessible(Integer storageId, String path) {
    method checkFileIsHidden (line 180) | public boolean checkFileIsHidden(Integer storageId, String fileName) {
    method checkFileIsDisableDownload (line 197) | public boolean checkFileIsDisableDownload(Integer storageId, String fi...
    method testPattern (line 220) | private boolean testPattern(Integer storageId, List<FilterConfig> patt...
    method onStorageSourceCopy (line 273) | @EventListener

FILE: src/main/java/im/zhaojun/zfile/module/install/controller/InstallController.java
  class InstallController (line 18) | @Tag(name = "初始化模块")
    method isInstall (line 26) | @GetMapping("/install/status")
    method install (line 33) | @ApiOperationSupport(order = 2)

FILE: src/main/java/im/zhaojun/zfile/module/install/model/request/InstallSystemRequest.java
  class InstallSystemRequest (line 11) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/install/service/InstallService.java
  class InstallService (line 15) | @Service
    method install (line 25) | @Transactional(rollbackFor = Exception.class)
    method getSystemIsInstalled (line 48) | public Boolean getSystemIsInstalled() {

FILE: src/main/java/im/zhaojun/zfile/module/link/aspect/LinkRateLimiterAspect.java
  class LinkRateLimiterAspect (line 27) | @Aspect
    method around (line 49) | @Around(value = "@annotation(im.zhaojun.zfile.module.storage.annotatio...

FILE: src/main/java/im/zhaojun/zfile/module/link/aspect/RefererCheckAspect.java
  class RefererCheckAspect (line 30) | @Aspect
    method around (line 54) | @Around(value = "@annotation(im.zhaojun.zfile.module.storage.annotatio...
    method containsPathMatcher (line 115) | public String containsPathMatcher(Collection<String> patternList, Stri...

FILE: src/main/java/im/zhaojun/zfile/module/link/cache/LinkRateLimiterCache.java
  class LinkRateLimiterCache (line 16) | @Service
    method containsKey (line 26) | public boolean containsKey(String key) {
    method get (line 30) | public AtomicInteger get(String key, boolean isUpdateLastAccess) {
    method put (line 34) | public void put(String key, AtomicInteger object, long timeout) {
    method getCacheInfo (line 38) | public List<CacheInfo<String, AtomicInteger>> getCacheInfo() {

FILE: src/main/java/im/zhaojun/zfile/module/link/controller/DirectLinkController.java
  class DirectLinkController (line 45) | @Tag(name = "短链")
    method init (line 62) | @EventListener(ApplicationReadyEvent.class)
    method directLink (line 76) | public ResponseEntity<?> directLink(@PathVariable("storageKey") String...
    method generatorShortLink (line 88) | @PostMapping("/api/path-link/batch/generate")

FILE: src/main/java/im/zhaojun/zfile/module/link/controller/ShortLinkController.java
  class ShortLinkController (line 38) | @Tag(name = "直短链模块")
    method generatorShortLink (line 56) | @PostMapping("/api/short-link/batch/generate")
    method parseShortKey (line 82) | @GetMapping("/s/{key}")

FILE: src/main/java/im/zhaojun/zfile/module/link/controller/ShortLinkManagerController.java
  class ShortLinkManagerController (line 53) | @Tag(name = "直链管理")
    method list (line 75) | @ApiOperationSupport(order = 1)
    method deleteById (line 91) | @ApiOperationSupport(order = 2)
    method batchDelete (line 103) | @ApiOperationSupport(order = 3)
    method exportExcel (line 113) | @ApiOperationSupport(order = 4)
    method getLinkLimitInfo (line 149) | @ApiOperationSupport(order = 5)
    method getShortLinkResultPage (line 157) | @NotNull
    method deleteExpireLink (line 192) | @ApiOperationSupport(order = 6)

FILE: src/main/java/im/zhaojun/zfile/module/link/convert/ShortLinkConvert.java
  type ShortLinkConvert (line 16) | @Component
    method entityToResultList (line 20) | @Mapping(source = "shortLink.id", target = "id")

FILE: src/main/java/im/zhaojun/zfile/module/link/dto/DynamicRegisterMappingHandlerDTO.java
  class DynamicRegisterMappingHandlerDTO (line 9) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/link/event/DeleteExpireLinkEvent.java
  class DeleteExpireLinkEvent (line 5) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/link/mapper/ShortLinkMapper.java
  type ShortLinkMapper (line 16) | @Mapper
    method findByKey (line 27) | ShortLink findByKey(@Param("key")String key);
    method deleteByStorageId (line 36) | int deleteByStorageId(@Param("storageId") Integer storageId);
    method findByStorageIdAndUrl (line 41) | ShortLink findByStorageIdAndUrl(@Param("storageId") Integer storageId,
    method deleteExpireLink (line 50) | int deleteExpireLink();

FILE: src/main/java/im/zhaojun/zfile/module/link/model/dto/CacheInfo.java
  class CacheInfo (line 7) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/link/model/entity/ShortLink.java
  class ShortLink (line 19) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/link/model/enums/RefererTypeEnum.java
  type RefererTypeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/link/model/request/BatchDeleteRequest.java
  class BatchDeleteRequest (line 10) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/link/model/request/BatchGenerateLinkRequest.java
  class BatchGenerateLinkRequest (line 15) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/link/model/request/QueryDownloadLogRequest.java
  class QueryDownloadLogRequest (line 18) | @Data
    method getDateFrom (line 50) | public Date getDateFrom() {
    method getDateTo (line 57) | public Date getDateTo() {

FILE: src/main/java/im/zhaojun/zfile/module/link/model/request/QueryLoginLogRequest.java
  class QueryLoginLogRequest (line 18) | @Data
    method getDateFrom (line 47) | public Date getDateFrom() {
    method getDateTo (line 54) | public Date getDateTo() {

FILE: src/main/java/im/zhaojun/zfile/module/link/model/request/QueryShortLinkLogRequest.java
  class QueryShortLinkLogRequest (line 16) | @Data
    method getDateFrom (line 33) | public Date getDateFrom() {
    method getDateTo (line 40) | public Date getDateTo() {

FILE: src/main/java/im/zhaojun/zfile/module/link/model/request/ShortLinkResult.java
  class ShortLinkResult (line 15) | @Data
    method getStorageTypeStr (line 30) | public String getStorageTypeStr() {

FILE: src/main/java/im/zhaojun/zfile/module/link/model/request/ShortLinkSearchRequest.java
  class ShortLinkSearchRequest (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/link/model/result/BatchGenerateLinkResponse.java
  class BatchGenerateLinkResponse (line 10) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/link/service/DynamicDirectLinkPrefixService.java
  class DynamicDirectLinkPrefixService (line 19) | @Slf4j
    method registerMappingHandlerMapping (line 28) | public void registerMappingHandlerMapping(String key, RequestMappingIn...
    method updateRegisterMappingHandler (line 34) | public void updateRegisterMappingHandler(String key, RequestMappingInf...

FILE: src/main/java/im/zhaojun/zfile/module/link/service/LinkDownloadService.java
  class LinkDownloadService (line 43) | @Slf4j
    method handlerDirectLink (line 64) | @RefererCheck
    method handlerShortLink (line 75) | @RefererCheck
    method handlerDownloadGetUrl (line 127) | private ResponseEntity<?> handlerDownloadGetUrl(String storageKey, Str...

FILE: src/main/java/im/zhaojun/zfile/module/link/service/ShortLinkService.java
  class ShortLinkService (line 36) | @Service
    method findByKey (line 58) | @Cacheable(key = "#key", unless = "#result == null", condition = "#key...
    method findByStorageIdAndUrl (line 74) | public @Nullable ShortLink findByStorageIdAndUrl(Integer storageId, St...
    method generatorShortLink (line 90) | public ShortLink generatorShortLink(Integer storageId, String fullPath...
    method deleteExpireLink (line 136) | @CacheEvict(allEntries = true)
    method removeById (line 144) | @CacheEvict(allEntries = true)
    method removeBatchByIds (line 150) | @Transactional(rollbackFor = Exception.class)
    method deleteByStorageId (line 157) | @CacheEvict(allEntries = true)
    method onStorageSourceDelete (line 170) | @EventListener
    method selectPage (line 183) | public Page<ShortLink> selectPage(Page<ShortLink> pages, Wrapper<Short...
    method checkExpireDateIsValidate (line 187) | private boolean checkExpireDateIsValidate(Long expires) {

FILE: src/main/java/im/zhaojun/zfile/module/log/controller/DownloadLogManagerController.java
  class DownloadLogManagerController (line 42) | @Tag(name = "直链日志管理")
    method list (line 60) | @ApiOperationSupport(order = 1)
    method deleteById (line 109) | @ApiOperationSupport(order = 2)
    method batchDelete (line 121) | @ApiOperationSupport(order = 3)
    method batchDeleteBySearchParams (line 132) | @ApiOperationSupport(order = 4)

FILE: src/main/java/im/zhaojun/zfile/module/log/controller/LoginLogController.java
  class LoginLogController (line 30) | @Tag(name = "登录日志管理")
    method list (line 39) | @ApiOperationSupport(order = 1)

FILE: src/main/java/im/zhaojun/zfile/module/log/convert/DownloadLogConvert.java
  type DownloadLogConvert (line 15) | @Component
    method entityToResultList (line 19) | @Mapping(source = "downloadLog.id", target = "id")

FILE: src/main/java/im/zhaojun/zfile/module/log/mapper/DownloadLogMapper.java
  type DownloadLogMapper (line 12) | @Mapper
    method deleteByStorageKey (line 21) | int deleteByStorageKey(String storageKey);
    method deleteExpireShortLinkLog (line 29) | int deleteExpireShortLinkLog();

FILE: src/main/java/im/zhaojun/zfile/module/log/mapper/LoginLogMapper.java
  type LoginLogMapper (line 7) | @Mapper

FILE: src/main/java/im/zhaojun/zfile/module/log/model/entity/DownloadLog.java
  class DownloadLog (line 24) | @Data
    method DownloadLog (line 80) | public DownloadLog(String downloadType, String path, String storageKey...

FILE: src/main/java/im/zhaojun/zfile/module/log/model/entity/LoginLog.java
  class LoginLog (line 9) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/log/model/result/DownloadLogResult.java
  class DownloadLogResult (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/log/service/DownloadLogService.java
  class DownloadLogService (line 23) | @Slf4j
    method save (line 30) | public void save(DownloadLog downloadLog) {
    method selectPage (line 34) | public Page<DownloadLog> selectPage(Page<DownloadLog> pages, Wrapper<D...
    method removeById (line 38) | public void removeById(Integer id) {
    method removeBatchByIds (line 42) | @Transactional(rollbackFor = Exception.class)
    method deleteByQueryWrapper (line 47) | public void deleteByQueryWrapper(Wrapper<DownloadLog> queryWrapper) {
    method deleteByStorageKey (line 51) | public int deleteByStorageKey(String storageKey) {
    method onStorageSourceDelete (line 63) | @EventListener
    method deleteExpireShortLinkLog (line 82) | public int deleteExpireShortLinkLog() {
    method deleteExpireShortLinkLog (line 86) | @EventListener(classes = DeleteExpireLinkEvent.class)

FILE: src/main/java/im/zhaojun/zfile/module/log/service/LoginLogService.java
  class LoginLogService (line 11) | @Service
    method save (line 17) | public void save(LoginLog loginLog) {
    method selectPage (line 21) | public Page<LoginLog> selectPage(Page<LoginLog> pages, Wrapper<LoginLo...

FILE: src/main/java/im/zhaojun/zfile/module/onlyoffice/controller/OnlyOfficeController.java
  class OnlyOfficeController (line 46) | @Slf4j
    method getPreviewFileJSONInfo (line 67) | @ApiOperationSupport(order = 3)
    method getFileInfo (line 88) | private Pair<FileItemResult, Boolean> getFileInfo(FileItemRequest file...
    method createOnlyOfficePayload (line 131) | private JSONObject createOnlyOfficePayload(FileItemResult fileItemResu...
    method callBack (line 166) | @RequestMapping("/callback")

FILE: src/main/java/im/zhaojun/zfile/module/onlyoffice/model/OnlyOfficeCallback.java
  class OnlyOfficeCallback (line 10) | @Data
    class Action (line 77) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/onlyoffice/model/OnlyOfficeFile.java
  class OnlyOfficeFile (line 6) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/password/controller/StorageSourcePasswordController.java
  class StorageSourcePasswordController (line 24) | @Tag(name = "存储源模块-密码文件夹")
    method getPasswordList (line 33) | @ApiOperationSupport(order = 1)
    method savePasswordList (line 42) | @ApiOperationSupport(order = 2)

FILE: src/main/java/im/zhaojun/zfile/module/password/mapper/PasswordConfigMapper.java
  type PasswordConfigMapper (line 15) | @Mapper
    method findByStorageId (line 26) | List<PasswordConfig> findByStorageId(@Param("storageId") Integer stora...
    method deleteByStorageId (line 37) | int deleteByStorageId(@Param("storageId") Integer storageId);

FILE: src/main/java/im/zhaojun/zfile/module/password/model/dto/VerifyResultDTO.java
  class VerifyResultDTO (line 11) | @Data
    method success (line 29) | public static VerifyResultDTO success() {
    method success (line 36) | public static VerifyResultDTO success(String pattern) {
    method fail (line 44) | public static VerifyResultDTO fail(ErrorCode errorCode) {

FILE: src/main/java/im/zhaojun/zfile/module/password/model/entity/PasswordConfig.java
  class PasswordConfig (line 18) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/password/service/PasswordConfigService.java
  class PasswordConfigService (line 34) | @Service
    method findByStorageId (line 54) | @Cacheable(key = "#storageId",
    method batchSave (line 70) | @Transactional(rollbackFor = Exception.class)
    method deleteByStorageId (line 95) | @CacheEvict(key = "#storageId")
    method onStorageSourceDelete (line 108) | @EventListener
    method verifyPassword (line 135) | public VerifyResultDTO verifyPassword(Integer storageId, String path, ...
    method matchPassword (line 228) | private boolean matchPassword(String expectedPasswordContent, String p...
    method onStorageSourceCopy (line 250) | @EventListener

FILE: src/main/java/im/zhaojun/zfile/module/permission/controller/PermissionController.java
  class PermissionController (line 14) | @Tag(name = "权限模块")
    method list (line 20) | @GetMapping("list")

FILE: src/main/java/im/zhaojun/zfile/module/permission/controller/StorageSourcePermissionController.java
  class StorageSourcePermissionController (line 28) | @Tag(name = "存储源模块-权限控制")
    method getPermissionList (line 40) | @ApiOperationSupport(order = 1)

FILE: src/main/java/im/zhaojun/zfile/module/permission/convert/PermissionConfigConvert.java
  type PermissionConfigConvert (line 16) | @Component
    method toResult (line 20) | List<PermissionConfigResult> toResult(List<PermissionConfig> permissio...

FILE: src/main/java/im/zhaojun/zfile/module/permission/mapper/PermissionConfigMapper.java
  type PermissionConfigMapper (line 15) | @Mapper
    method findByStorageId (line 26) | List<PermissionConfig> findByStorageId(@Param("storageId") Integer sto...
    method deleteByStorageId (line 37) | int deleteByStorageId(@Param("storageId") Integer storageId);

FILE: src/main/java/im/zhaojun/zfile/module/permission/model/entity/PermissionConfig.java
  class PermissionConfig (line 17) | @Data
    method getDefaultInstance (line 50) | public static PermissionConfig getDefaultInstance(Integer storageId, F...

FILE: src/main/java/im/zhaojun/zfile/module/permission/model/result/PermissionConfigResult.java
  class PermissionConfigResult (line 9) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/permission/model/result/PermissionInfoResult.java
  class PermissionInfoResult (line 6) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/permission/service/PermissionConfigService.java
  class PermissionConfigService (line 24) | @Service
    method findByStorageId (line 41) | @Deprecated
    method findByStorageIdNotThreadSafe (line 57) | @Transactional(rollbackFor = Exception.class)
    method deleteByStorageId (line 76) | @Deprecated
    method onStorageSourceDelete (line 91) | @EventListener

FILE: src/main/java/im/zhaojun/zfile/module/readme/controller/StorageSourceReadmeController.java
  class StorageSourceReadmeController (line 24) | @Tag(name = "存储源模块-README")
    method getReadmeList (line 33) | @ApiOperationSupport(order = 1)
    method saveReadmeList (line 42) | @ApiOperationSupport(order = 2)

FILE: src/main/java/im/zhaojun/zfile/module/readme/mapper/ReadmeConfigMapper.java
  type ReadmeConfigMapper (line 15) | @Mapper
    method findByStorageId (line 27) | List<ReadmeConfig> findByStorageId(@Param("storageId") Integer storage...
    method deleteByStorageId (line 38) | int deleteByStorageId(@Param("storageId") Integer storageId);

FILE: src/main/java/im/zhaojun/zfile/module/readme/model/entity/ReadmeConfig.java
  class ReadmeConfig (line 19) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/readme/model/enums/ReadmeDisplayModeEnum.java
  type ReadmeDisplayModeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/readme/model/enums/ReadmePathModeEnum.java
  type ReadmePathModeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/readme/service/ReadmeConfigService.java
  class ReadmeConfigService (line 36) | @Slf4j
    method findByStorageId (line 52) | @Cacheable(key = "#storageId",
    method batchSave (line 68) | @Transactional(rollbackFor = Exception.class)
    method deleteByStorageId (line 94) | @CacheEvict(key = "#storageId")
    method onStorageSourceDelete (line 107) | @EventListener
    method findReadmeByPath (line 131) | public ReadmeConfig findReadmeByPath(Integer storageId, String path) {
    method getByStorageAndPath (line 151) | public ReadmeConfig getByStorageAndPath(Integer storageId, String path...
    method getReadmeByTestPattern (line 194) | private ReadmeConfig getReadmeByTestPattern(Integer storageId, List<Re...
    method onStorageSourceCopy (line 247) | @EventListener

FILE: src/main/java/im/zhaojun/zfile/module/share/context/ShareAccessContext.java
  class ShareAccessContext (line 13) | @Component
    class ShareAccessInfo (line 21) | @Getter
    method setShareAccess (line 36) | public static void setShareAccess(String shareKey, String shareBasePat...
    method setShareAccess (line 47) | public static void setShareAccess(String shareKey, String shareBasePat...
    method isShareAccess (line 56) | public static boolean isShareAccess() {
    method getShareBasePath (line 66) | public static String getShareBasePath() {
    method getShareKey (line 76) | public static String getShareKey() {
    method getShareUserId (line 86) | public static Integer getShareUserId() {
    method clear (line 95) | public static void clear() {
    method getCurrentInfo (line 104) | public static ShareAccessInfo getCurrentInfo() {

FILE: src/main/java/im/zhaojun/zfile/module/share/controller/ShareFileManagerController.java
  class ShareFileManagerController (line 24) | @Tag(name = "分享管理模块")
    method getShareList (line 33) | @ApiOperationSupport(order = 1)
    method deleteExpiredShares (line 41) | @ApiOperationSupport(order = 2)

FILE: src/main/java/im/zhaojun/zfile/module/share/controller/ShareLinkController.java
  class ShareLinkController (line 39) | @Tag(name = "分享文件模块")
    method createShareLink (line 52) | @ApiOperationSupport(order = 1)
    method getShareInfo (line 61) | @ApiOperationSupport(order = 2)
    method verifyPassword (line 70) | @ApiOperationSupport(order = 3)
    method getShareFileList (line 78) | @ApiOperationSupport(order = 4)
    method downloadShareFile (line 97) | @ApiOperationSupport(order = 5)
    method deleteShare (line 125) | @ApiOperationSupport(order = 6)
    method getUserShareList (line 135) | @ApiOperationSupport(order = 7)
    method deleteExpiredShares (line 143) | @ApiOperationSupport(order = 8)

FILE: src/main/java/im/zhaojun/zfile/module/share/mapper/ShareLinkMapper.java
  type ShareLinkMapper (line 11) | @Mapper
    method getByShareKey (line 17) | ShareLink getByShareKey(@Param("shareKey") String shareKey);
    method getByUserId (line 22) | List<ShareLink> getByUserId(@Param("userId") Integer userId);
    method incrementAccessCount (line 27) | int incrementAccessCount(@Param("shareKey") String shareKey, @Param("i...
    method incrementDownloadCount (line 32) | int incrementDownloadCount(@Param("shareKey") String shareKey, @Param(...
    method deleteExpiredLinks (line 37) | int deleteExpiredLinks(@Param("currentTime") Date currentTime);
    method deleteExpiredLinksByUserId (line 42) | int deleteExpiredLinksByUserId(@Param("userId") Integer userId, @Param...

FILE: src/main/java/im/zhaojun/zfile/module/share/model/dto/ShareEntryDTO.java
  class ShareEntryDTO (line 15) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/share/model/entity/ShareLink.java
  class ShareLink (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/share/model/enums/ShareEntryTypeEnum.java
  type ShareEntryTypeEnum (line 12) | @Schema(description = "分享条目类型")

FILE: src/main/java/im/zhaojun/zfile/module/share/model/enums/ShareTypeEnum.java
  type ShareTypeEnum (line 8) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/share/model/request/CreateShareLinkRequest.java
  class CreateShareLinkRequest (line 19) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/share/model/request/ShareFileListRequest.java
  class ShareFileListRequest (line 14) | @Data
    method handleDefaultValue (line 37) | public void handleDefaultValue() {

FILE: src/main/java/im/zhaojun/zfile/module/share/model/request/ShareLinkListRequest.java
  class ShareLinkListRequest (line 15) | @EqualsAndHashCode(callSuper = true)
    method handleDefaultValue (line 52) | public void handleDefaultValue() {

FILE: src/main/java/im/zhaojun/zfile/module/share/model/request/VerifySharePasswordRequest.java
  class VerifySharePasswordRequest (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/share/model/result/CreateShareLinkResult.java
  class CreateShareLinkResult (line 11) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/share/model/result/ShareFileInfoResult.java
  class ShareFileInfoResult (line 15) | @Data
    method ShareFileInfoResult (line 37) | public ShareFileInfoResult() {
    method ShareFileInfoResult (line 40) | public ShareFileInfoResult(List<FileItemResult> fileItemList, String c...

FILE: src/main/java/im/zhaojun/zfile/module/share/model/result/ShareLinkResult.java
  class ShareLinkResult (line 17) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/share/service/ShareLinkFileService.java
  class ShareLinkFileService (line 38) | @Slf4j
    method getShareFileList (line 59) | public ShareFileInfoResult getShareFileList(String shareKey, String re...
    method getShareFileDownloadUrl (line 124) | public String getShareFileDownloadUrl(String shareKey, String filePath...
    method getShareFileItem (line 157) | public FileItemResult getShareFileItem(String shareKey, String filePat...
    method getValidShareLink (line 188) | private ShareLink getValidShareLink(String shareKey) {
    method validateSharePassword (line 198) | private void validateSharePassword(ShareLink shareLink, String passwor...
    method getFilteredFileList (line 216) | private List<FileItemResult> getFilteredFileList(ShareLink shareLink, ...
    method getFilteredRootFileList (line 233) | private List<FileItemResult> getFilteredRootFileList(ShareLink shareLi...
    method filterByShareEntries (line 248) | private List<FileItemResult> filterByShareEntries(List<FileItemResult>...
    method parseShareEntries (line 273) | private List<ShareEntryDTO> parseShareEntries(String shareItemJson) {

FILE: src/main/java/im/zhaojun/zfile/module/share/service/ShareLinkService.java
  class ShareLinkService (line 48) | @Slf4j
    method initBufferFlushScheduler (line 84) | @PostConstruct
    method shutdownBufferFlushScheduler (line 97) | @PreDestroy
    method getByShareKey (line 116) | @Cacheable(key = "'shareKey:' + #shareKey", condition = "#shareKey != ...
    method deleteShareLink (line 124) | @CacheEvict(key = "'shareKey:' + #shareKey")
    method deleteExpiredLinks (line 142) | @CacheEvict(allEntries = true)
    method deleteExpiredLinksByUserId (line 150) | @CacheEvict(allEntries = true)
    method incrementAccessCount (line 161) | public void incrementAccessCount(String shareKey) {
    method incrementDownloadCount (line 169) | public void incrementDownloadCount(String shareKey) {
    method bufferIncrement (line 174) | private void bufferIncrement(String shareKey,
    method flushAllBuffersSafely (line 188) | private void flushAllBuffersSafely() {
    method flushBuffer (line 199) | private void flushBuffer(ConcurrentMap<String, AtomicInteger> buffer,
    method drainSingleEntry (line 207) | private void drainSingleEntry(String shareKey,
    method createShareLink (line 236) | public CreateShareLinkResult createShareLink(CreateShareLinkRequest re...
    method getShareLinkInfo (line 281) | public ShareLinkResult getShareLinkInfo(String shareKey) {
    method verifyPassword (line 289) | public boolean verifyPassword(String shareKey, String password) {
    method getValidShareLink (line 306) | public ShareLink getValidShareLink(String shareKey) {
    method getUserShareList (line 324) | public Page<ShareLinkResult> getUserShareList(ShareLinkListRequest req...
    method getAdminShareList (line 346) | public Page<ShareLinkResult> getAdminShareList(ShareLinkListRequest re...
    method buildShareListQueryWrapper (line 354) | private LambdaQueryWrapper<ShareLink> buildShareListQueryWrapper(Share...
    method buildShareResultPage (line 392) | private Page<ShareLinkResult> buildShareResultPage(Page<ShareLink> pag...
    method isExpired (line 437) | private boolean isExpired(ShareLink shareLink) {
    method buildShareLinkResult (line 450) | private ShareLinkResult buildShareLinkResult(ShareLink shareLink, bool...
    method validateAndGetFileService (line 492) | private AbstractBaseFileService<?> validateAndGetFileService(CreateSha...
    method generateOrValidateShareKey (line 520) | private String generateOrValidateShareKey(String customShareKey, Strin...
    method validateCustomShareKey (line 537) | private String validateCustomShareKey(String customShareKey, String st...
    method generateShareKey (line 560) | private String generateShareKey() {
    method hasCustomKeyPermission (line 574) | private boolean hasCustomKeyPermission(Integer storageId) {

FILE: src/main/java/im/zhaojun/zfile/module/sso/controller/SsoAPIController.java
  class SsoAPIController (line 21) | @Slf4j
    method list (line 30) | @GetMapping("/list")

FILE: src/main/java/im/zhaojun/zfile/module/sso/controller/SsoController.java
  class SsoController (line 20) | @Slf4j
    method login (line 29) | @GetMapping("/{provider}/login")
    method callback (line 43) | @GetMapping("/{provider}/login/callback")

FILE: src/main/java/im/zhaojun/zfile/module/sso/controller/SsoManagerController.java
  class SsoManagerController (line 23) | @Slf4j
    method list (line 32) | @GetMapping("/providers")
    method saveOrUpdateProvider (line 39) | @PostMapping("/provider")
    method deleteProvider (line 46) | @DeleteMapping("/provider/{provider}")
    method checkDuplicate (line 54) | @GetMapping("/provider/checkDuplicate")

FILE: src/main/java/im/zhaojun/zfile/module/sso/mapper/SsoConfigMapper.java
  type SsoConfigMapper (line 16) | @Mapper
    method findAll (line 19) | List<SsoConfig> findAll();
    method findAllLoginItems (line 21) | List<SsoLoginItemResponse> findAllLoginItems();
    method findByProvider (line 23) | SsoConfig findByProvider(@Param("provider") String provider);
    method countByProvider (line 25) | int countByProvider(@Param("provider") String provider, @Param("ignore...

FILE: src/main/java/im/zhaojun/zfile/module/sso/model/entity/SsoConfig.java
  class SsoConfig (line 20) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/sso/model/request/CheckProviderDuplicateRequest.java
  class CheckProviderDuplicateRequest (line 6) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/sso/model/response/SsoLoginItemResponse.java
  class SsoLoginItemResponse (line 6) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/sso/model/response/TokenResponse.java
  class TokenResponse (line 5) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/sso/service/SsoService.java
  class SsoService (line 45) | @Slf4j
    method list (line 61) | public List<SsoConfig> list() {
    method listAllLoginItems (line 65) | public List<SsoLoginItemResponse> listAllLoginItems() {
    method getProvider (line 69) | @Cacheable(key = "#provider", unless = "#result == null", condition = ...
    method saveOrUpdate (line 74) | @CacheEvict(key = "#result.provider")
    method deleteProvider (line 89) | @CacheEvict(key = "#provider")
    method checkDuplicateProvider (line 94) | public boolean checkDuplicateProvider(Integer ignoreId, String provide...
    method getAuthRedirectUrl (line 105) | public String getAuthRedirectUrl(String provider, String state) {
    method callbackHandler (line 134) | public String callbackHandler(String provider, String code) {

FILE: src/main/java/im/zhaojun/zfile/module/storage/annotation/StorageParamSelect.java
  type StorageParamSelect (line 13) | public interface StorageParamSelect {
    method getOptions (line 26) | List<StorageSourceParamDef.Options> getOptions(StorageParamItem storag...

FILE: src/main/java/im/zhaojun/zfile/module/storage/annotation/impl/EncodingStorageParamSelect.java
  class EncodingStorageParamSelect (line 17) | public class EncodingStorageParamSelect implements StorageParamSelect {
    method getOptions (line 19) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/aspect/CheckPasswordAspect.java
  class CheckPasswordAspect (line 35) | @Aspect
    method around (line 54) | @Around(value = "@annotation(im.zhaojun.zfile.module.storage.annotatio...
    method getFieldValue (line 105) | public String getFieldValue(Object target, String expression) {

FILE: src/main/java/im/zhaojun/zfile/module/storage/aspect/FileOperatorCheckAspect.java
  class FileOperatorCheckAspect (line 34) | @Aspect
    method annotationCheck (line 53) | @Around("@annotation(storagePermissionCheck)")
    method linkActionCheck (line 63) | public Object linkActionCheck(ProceedingJoinPoint point) throws Throwa...
    method availableAround (line 85) | @Around("execution(public * im.zhaojun.zfile.module.storage.service.ba...
    method newFolderAround (line 100) | @Around("execution(public * im.zhaojun.zfile.module.storage.service.ba...
    method deleteAround (line 114) | @Around("execution(public * im.zhaojun.zfile.module.storage.service.ba...
    method uploadAround (line 139) | @Around("execution(public * im.zhaojun.zfile.module.storage.service.ba...
    method renameAround (line 172) | @Around("execution(public * im.zhaojun.zfile.module.storage.service.ba...
    method moveAround (line 200) | @Around("execution(public * im.zhaojun.zfile.module.storage.service.ba...
    method copyAround (line 223) | @Around("execution(public * im.zhaojun.zfile.module.storage.service.ba...
    method checkPermission (line 238) | private void checkPermission(ProceedingJoinPoint point, FileOperatorTy...
    method allowAccess (line 251) | private boolean allowAccess(Integer storageId, FileOperatorTypeEnum fi...
    method clearOnlyOfficeCache (line 282) | private void clearOnlyOfficeCache(String fullPath, Integer storageId, ...

FILE: src/main/java/im/zhaojun/zfile/module/storage/chain/FileChain.java
  class FileChain (line 16) | @Service
    method init (line 38) | @PostConstruct
    method execute (line 55) | public FileContext execute(FileContext content) throws Exception {

FILE: src/main/java/im/zhaojun/zfile/module/storage/chain/FileContext.java
  class FileContext (line 19) | @EqualsAndHashCode(callSuper = true)

FILE: src/main/java/im/zhaojun/zfile/module/storage/chain/command/FileAccessPermissionVerifyCommand.java
  class FileAccessPermissionVerifyCommand (line 19) | @Service
    method execute (line 33) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/chain/command/FileDownloadPermissionCommand.java
  class FileDownloadPermissionCommand (line 18) | @Service
    method execute (line 32) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/chain/command/FileHiddenCommand.java
  class FileHiddenCommand (line 21) | @Service
    method execute (line 35) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/chain/command/FileSortCommand.java
  class FileSortCommand (line 20) | @Service
    method execute (line 31) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/chain/command/FolderPasswordVerifyCommand.java
  class FolderPasswordVerifyCommand (line 24) | @Service
    method execute (line 41) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/constant/S3SignerTypeConstant.java
  class S3SignerTypeConstant (line 6) | public class S3SignerTypeConstant {

FILE: src/main/java/im/zhaojun/zfile/module/storage/constant/StorageConfigConstant.java
  class StorageConfigConstant (line 8) | public class StorageConfigConstant {

FILE: src/main/java/im/zhaojun/zfile/module/storage/constant/StorageSourceConnectionProperties.java
  class StorageSourceConnectionProperties (line 3) | public class StorageSourceConnectionProperties {

FILE: src/main/java/im/zhaojun/zfile/module/storage/context/StorageSourceContext.java
  class StorageSourceContext (line 35) | @Slf4j
    method load (line 70) | static void load(Map<String, AbstractBaseFileService> storageTypeServi...
    method getByStorageId (line 86) | public static AbstractBaseFileService<IStorageParam> getByStorageId(In...
    method getByStorageKey (line 103) | public static AbstractBaseFileService<?> getByStorageKey(String key) {
    method getByStorageTypeEnum (line 120) | public static AbstractBaseFileService<?> getByStorageTypeEnum(StorageT...
    method getStorageSourceParamListByType (line 133) | public static List<StorageSourceParamDef> getStorageSourceParamListByT...
    method init (line 151) | public static void init(StorageSourceInitDTO storageSourceInitDTO) {
    method getInitStorageBeanByStorageType (line 182) | private static AbstractBaseFileService<IStorageParam> getInitStorageBe...
    method getInitParam (line 195) | private static IStorageParam getInitParam(AbstractBaseFileService<?> b...
    method getAllRefreshTokenStorageSource (line 286) | public static Map<Integer, RefreshTokenService> getAllRefreshTokenStor...
    method destroy (line 312) | public static void destroy(StorageSource storageSource) {

FILE: src/main/java/im/zhaojun/zfile/module/storage/context/StorageSourceInitializer.java
  class StorageSourceInitializer (line 24) | @Slf4j
    method setApplicationContext (line 36) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/base/StorageMetaDataController.java
  class StorageMetaDataController (line 22) | @Tag(name = "存储源模块-元数据")
    method supportStorage (line 28) | @GetMapping("/support-storage")
    method getFormByStorageType (line 36) | @GetMapping("/storage-params")

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/base/StorageSourceController.java
  class StorageSourceController (line 33) | @Tag(name = "存储源模块-基础")
    method storageList (line 46) | @ApiOperationSupport(order = 1)
    method storageItem (line 63) | @ApiOperationSupport(order = 2)
    method saveStorageItem (line 73) | @ApiOperationSupport(order = 3)
    method deleteStorageItem (line 83) | @ApiOperationSupport(order = 4)
    method enable (line 94) | @ApiOperationSupport(order = 5)
    method disable (line 107) | @ApiOperationSupport(order = 6)
    method updateStorageSort (line 120) | @ApiOperationSupport(order = 7)
    method existKey (line 129) | @ApiOperationSupport(order = 8)
    method changeCompatibilityReadme (line 139) | @ApiOperationSupport(order = 9)
    method copyStorage (line 154) | @ApiOperationSupport(order = 10)

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/callback/GoogleDriveCallbackController.java
  class GoogleDriveCallbackController (line 20) | @Tag(name = "Google Drive 认证回调模块")
    method authorize (line 29) | @GetMapping("/authorize")
    method googleDriveCallback (line 37) | @GetMapping("/callback")

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/callback/OneDriveCallbackController.java
  class OneDriveCallbackController (line 23) | @Tag(name = "OneDrive 认证回调模块")
    method authorize (line 35) | @GetMapping("/authorize")
    method oneDriveCallback (line 44) | @GetMapping("/callback")
    method authorizeChina (line 68) | @GetMapping("/china-authorize")
    method oneDriveChinaCallback (line 77) | @GetMapping("/china-callback")

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/file/FileController.java
  class FileController (line 39) | @Tag(name = "文件列表模块")
    method storageList (line 56) | @ApiOperationSupport(order = 1)
    method list (line 68) | @ApiOperationSupport(order = 2)
    method fileItem (line 98) | @ApiOperationSupport(order = 3)

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/file/FileOperatorController.java
  class FileOperatorController (line 33) | @Tag(name = "文件操作模块")
    method mkdir (line 43) | @ApiOperationSupport(order = 1)
    method deleteFile (line 61) | @ApiOperationSupport(order = 2)
    method rename (line 128) | @ApiOperationSupport(order = 3)
    method renameFolder (line 146) | @ApiOperationSupport(order = 4)
    method getUploadFileUrl (line 163) | @ApiOperationSupport(order = 5)
    method moveFile (line 177) | @ApiOperationSupport(order = 6)

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/helper/GoogleDriveHelperController.java
  class GoogleDriveHelperController (line 28) | @Tag(name = "gd 工具辅助模块")
    method getDrives (line 33) | @PostMapping("/drives")

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/helper/Open115HelperController.java
  class Open115HelperController (line 21) | @Tag(name = "115 工具辅助模块")
    method generateQrCode (line 26) | @GetMapping("/qrcode")
    method getQrCodeStatus (line 53) | @PostMapping("/qrCodeStatus")

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/helper/Open115UploadUtils.java
  class Open115UploadUtils (line 33) | @Slf4j
    method uploadFile (line 75) | public static String uploadFile(File file, String fileName, String tar...
    method initUploadWithAuthHandling (line 98) | private static InitUploadResponse initUploadWithAuthHandling(File file...
    method uploadToObjectStorage (line 148) | private static void uploadToObjectStorage(File file, InitUploadRespons...
    method getUploadToken (line 173) | private static UploadTokenResponse getUploadToken(Supplier<String> acc...
    method calculateSha1 (line 198) | private static String calculateSha1(File file, long offset, long lengt...
    class InitUploadResponse (line 217) | public static class InitUploadResponse {
      method getData (line 223) | public InitUploadResponse.Data getData() {
      class Data (line 230) | public static class Data {
        method getCallback (line 255) | public CallbackInfo getCallback() {
    class CallbackInfo (line 265) | public static class CallbackInfo {
    class UploadTokenResponse (line 274) | public static class UploadTokenResponse {
      method getData (line 284) | public UploadTokenResponse.Data getData() {
      class Data (line 291) | public static class Data {
    class OssHeaderInterceptor (line 312) | static class OssHeaderInterceptor implements ExecutionInterceptor {
      method modifyHttpRequest (line 314) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/helper/S3HelperController.java
  class S3HelperController (line 40) | @Tag(name = "S3 工具辅助模块")
    method getBucketNames (line 45) | @PostMapping("/getBuckets")
    method getCorsConfig (line 101) | @PostMapping("/getCorsConfig")

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/helper/SharePointHelperController.java
  class SharePointHelperController (line 36) | @Tag(name = "SharePoint 工具辅助模块")
    method getSites (line 46) | @PostMapping("/getSites")
    method getSites (line 81) | @PostMapping("/getSiteLists")
    method getDomainPrefix (line 130) | @PostMapping("/getDomainPrefix")
    method getSiteId (line 159) | @PostMapping("/getSiteId")
    method getHostByType (line 226) | private String getHostByType(String type) {
    method getSearchSiteUrlByType (line 248) | private String getSearchSiteUrlByType(String type) {
    method getSiteListsUrlByType (line 264) | private String getSiteListsUrlByType(String type, String siteId) {
    method getSiteRootUrlByType (line 280) | private String getSiteRootUrlByType(String type) {
    method getBearer (line 294) | private static String getBearer(String accessToken) {

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/proxy/Open115UrlController.java
  class Open115UrlController (line 20) | @RestController
    method redirectTo115DownloadUrl (line 25) | @GetMapping(PROXY_DOWNLOAD_LINK_PREFIX + "{storageId}/{pickCode}")

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/proxy/ProxyDownloadController.java
  class ProxyDownloadController (line 34) | @Tag(name = "服务端代理下载")
    method downAttachment (line 39) | @GetMapping("/pd/{storageKey}/**")

FILE: src/main/java/im/zhaojun/zfile/module/storage/controller/proxy/ProxyUploadController.java
  class ProxyUploadController (line 22) | @Tag(name = "服务端代理上传")
    method upload (line 26) | @PutMapping("/file/upload/{storageKey}/**")

FILE: src/main/java/im/zhaojun/zfile/module/storage/convert/StorageSourceConvert.java
  type StorageSourceConvert (line 23) | @Component
    method entityToResultList (line 36) | List<StorageSourceResult> entityToResultList(List<StorageSource> list);
    method entityToConfigResult (line 47) | @Mapping(source = "readmeConfig.displayMode", target = "readmeDisplayM...
    method entityToAdminResultList (line 59) | List<StorageSourceAdminResult> entityToAdminResultList(List<StorageSou...
    method entityToDTO (line 62) | StorageSourceDTO entityToDTO(StorageSource storageSource, StorageSourc...
    method saveRequestToEntity (line 73) | StorageSource saveRequestToEntity(SaveStorageSourceRequest saveStorage...

FILE: src/main/java/im/zhaojun/zfile/module/storage/enums/StorageParamItemAnnoEnum.java
  type StorageParamItemAnnoEnum (line 4) | public enum StorageParamItemAnnoEnum {

FILE: src/main/java/im/zhaojun/zfile/module/storage/event/StorageSourceCopyEvent.java
  class StorageSourceCopyEvent (line 8) | @Data
    method StorageSourceCopyEvent (line 15) | public StorageSourceCopyEvent(Integer fromId, Integer newId) {

FILE: src/main/java/im/zhaojun/zfile/module/storage/event/StorageSourceDeleteEvent.java
  class StorageSourceDeleteEvent (line 10) | @Data
    method StorageSourceDeleteEvent (line 21) | public StorageSourceDeleteEvent(StorageSource storageSource) {

FILE: src/main/java/im/zhaojun/zfile/module/storage/function/AllowAdminFileOperatorTypeEnumDefaultValueFunc.java
  class AllowAdminFileOperatorTypeEnumDefaultValueFunc (line 12) | public class AllowAdminFileOperatorTypeEnumDefaultValueFunc implements F...
    method apply (line 22) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/function/AllowAllFileOperatorTypeEnumDefaultValueFunc.java
  class AllowAllFileOperatorTypeEnumDefaultValueFunc (line 12) | public class AllowAllFileOperatorTypeEnumDefaultValueFunc implements Fun...
    method apply (line 22) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/function/BasicFileOperatorTypeEnumDefaultValueFunc.java
  class BasicFileOperatorTypeEnumDefaultValueFunc (line 21) | public class BasicFileOperatorTypeEnumDefaultValueFunc implements Functi...
    method apply (line 35) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/function/DisableAllFileOperatorTypeEnumDefaultValueFunc.java
  class DisableAllFileOperatorTypeEnumDefaultValueFunc (line 12) | public class DisableAllFileOperatorTypeEnumDefaultValueFunc implements F...
    method apply (line 22) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/function/LinkFileOperatorTypeEnumDefaultValueFunc.java
  class LinkFileOperatorTypeEnumDefaultValueFunc (line 15) | public class LinkFileOperatorTypeEnumDefaultValueFunc implements Functio...
    method apply (line 27) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/function/SearchFileOperatorTypeEnumDefaultValueFunc.java
  class SearchFileOperatorTypeEnumDefaultValueFunc (line 13) | public class SearchFileOperatorTypeEnumDefaultValueFunc implements Funct...
    method apply (line 17) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/function/ShortLinkFileOperatorTypeEnumDefaultValueFunc.java
  class ShortLinkFileOperatorTypeEnumDefaultValueFunc (line 15) | public class ShortLinkFileOperatorTypeEnumDefaultValueFunc implements Fu...
    method apply (line 27) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/mapper/StorageSourceConfigMapper.java
  type StorageSourceConfigMapper (line 16) | @Mapper
    method findByStorageIdOrderById (line 27) | List<StorageSourceConfig> findByStorageIdOrderById(@Param("storageId")...
    method deleteByStorageId (line 38) | int deleteByStorageId(@Param("storageId") Integer storageId);
    method insertList (line 49) | int insertList(@Param("list") List<StorageSourceConfig> list);

FILE: src/main/java/im/zhaojun/zfile/module/storage/mapper/StorageSourceMapper.java
  type StorageSourceMapper (line 16) | @Mapper
    method findUserEnableList (line 24) | List<StorageSource> findUserEnableList(Integer userId);
    method findAllOrderByOrderNum (line 32) | List<StorageSource> findAllOrderByOrderNum();
    method selectMaxId (line 40) | Integer selectMaxId();
    method findByType (line 51) | List<StorageSource> findByType(@Param("type") StorageTypeEnum type);
    method updateSetOrderNumById (line 63) | void updateSetOrderNumById(@Param("orderNum") int orderNum, @Param("id...
    method findByStorageKey (line 74) | StorageSource findByStorageKey(@Param("storageKey") String storageKey);
    method findIdByStorageKey (line 85) | Integer findIdByStorageKey(@Param("storageKey") String storageKey);
    method findKeyById (line 96) | String findKeyById(@Param("id")Integer id);

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/bo/AuthModel.java
  class AuthModel (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/bo/RefreshTokenCacheBO.java
  class RefreshTokenCacheBO (line 16) | @ToString
    method putRefreshTokenInfo (line 21) | public static void putRefreshTokenInfo(Integer storageId, RefreshToken...
    method getRefreshTokenInfo (line 26) | public static RefreshTokenInfo getRefreshTokenInfo(Integer storageId) {
    class RefreshTokenInfo (line 30) | @Data
      method success (line 43) | public static RefreshTokenInfo success(RefreshTokenInfoDTO data) {
      method fail (line 51) | public static RefreshTokenInfo fail(String msg) {
      method isExpired (line 59) | public boolean isExpired() {

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/bo/StorageSourceMetadata.java
  class StorageSourceMetadata (line 8) | @Data
    type UploadType (line 41) | public enum UploadType {

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/bo/StorageSourceParamDef.java
  class StorageSourceParamDef (line 16) | @Data
    class Options (line 89) | @Getter
      method Options (line 96) | public Options(String value) {
      method Options (line 101) | public Options(String label, String value) {
      method Options (line 105) | public Options(StorageParamSelectOption storageParamSelectOption) {

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/bo/UploadSignParam.java
  class UploadSignParam (line 10) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/dto/FileOperatorTypeDefaultValueDTO.java
  class FileOperatorTypeDefaultValueDTO (line 11) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/dto/OAuth2TokenDTO.java
  class OAuth2TokenDTO (line 11) | @ToString
    method success (line 34) | public static OAuth2TokenDTO success(String clientId, String clientSec...
    method fail (line 47) | public static OAuth2TokenDTO fail(String clientId, String clientSecret...

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/dto/RefreshTokenInfoDTO.java
  class RefreshTokenInfoDTO (line 7) | @Data
    method getExpiredAtDate (line 30) | public Date getExpiredAtDate() {
    method success (line 43) | public static RefreshTokenInfoDTO success(String accessToken, String r...
    method success (line 61) | public static RefreshTokenInfoDTO success(String accessToken, String r...

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/dto/StorageSourceAllParamDTO.java
  class StorageSourceAllParamDTO (line 17) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/dto/StorageSourceDTO.java
  class StorageSourceDTO (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/dto/StorageSourceInitDTO.java
  class StorageSourceInitDTO (line 11) | @Data
    method convert (line 29) | public static StorageSourceInitDTO convert(StorageSource storageSource...

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/dto/ZFileCORSRule.java
  class ZFileCORSRule (line 11) | @Data
    method fromCORSRule (line 26) | public static List<ZFileCORSRule> fromCORSRule(List<CORSRule> corsRule...
    method toCORSRule (line 41) | public static Set<CORSRule> toCORSRule(List<ZFileCORSRule> zFileCORSRu...
    method sortAndDistinct (line 56) | public static List<ZFileCORSRule> sortAndDistinct(List<ZFileCORSRule> ...
    method equals (line 76) | @Override
    method hashCode (line 88) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/entity/StorageSource.java
  class StorageSource (line 19) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/entity/StorageSourceConfig.java
  class StorageSourceConfig (line 18) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/enums/FileOperatorTypeEnum.java
  type FileOperatorTypeEnum (line 22) | @Slf4j
    method getDefaultValue (line 163) | public FileOperatorTypeDefaultValueDTO getDefaultValue(Integer storage...
    method isDeprecated (line 171) | public boolean isDeprecated() {

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/enums/FileTypeEnum.java
  type FileTypeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/enums/SearchFolderModeEnum.java
  type SearchFolderModeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/enums/SearchModeEnum.java
  type SearchModeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/enums/StorageParamTypeEnum.java
  type StorageParamTypeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/enums/StorageTypeEnum.java
  type StorageTypeEnum (line 17) | @JsonFormat(shape = JsonFormat.Shape.OBJECT)
    method StorageTypeEnum (line 57) | StorageTypeEnum(String key, String description) {
    method getKey (line 62) | public String getKey() {
    method getDescription (line 66) | public String getDescription() {
    method getValue (line 70) | @JsonIgnore

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/AliyunParam.java
  class AliyunParam (line 11) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/DogeCloudParam.java
  class DogeCloudParam (line 11) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/FtpParam.java
  class FtpParam (line 16) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/GoogleDriveParam.java
  class GoogleDriveParam (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/HuaweiParam.java
  class HuaweiParam (line 10) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/IStorageParam.java
  type IStorageParam (line 6) | public interface IStorageParam {

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/LocalParam.java
  class LocalParam (line 12) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/MicrosoftDriveParam.java
  class MicrosoftDriveParam (line 12) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/MinIOParam.java
  class MinIOParam (line 12) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/OneDriveChinaParam.java
  class OneDriveChinaParam (line 11) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/OneDriveParam.java
  class OneDriveParam (line 10) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/Open115Param.java
  class Open115Param (line 7) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/OptionalProxyTransferParam.java
  class OptionalProxyTransferParam (line 8) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/ProxyTransferParam.java
  class ProxyTransferParam (line 12) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/QiniuParam.java
  class QiniuParam (line 10) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/S3BaseParam.java
  class S3BaseParam (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/S3Param.java
  class S3Param (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/SftpParam.java
  class SftpParam (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/SharePointChinaParam.java
  class SharePointChinaParam (line 11) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/SharePointParam.java
  class SharePointParam (line 11) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/TencentParam.java
  class TencentParam (line 12) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/UpYunParam.java
  class UpYunParam (line 11) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/param/WebdavParam.java
  class WebdavParam (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/GetGoogleDriveListRequest.java
  class GetGoogleDriveListRequest (line 11) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/GetS3BucketListRequest.java
  class GetS3BucketListRequest (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/GetS3CorsListRequest.java
  class GetS3CorsListRequest (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/SharePointInfoRequest.java
  class SharePointInfoRequest (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/SharePointSearchSitesRequest.java
  class SharePointSearchSitesRequest (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/SharePointSiteListsRequest.java
  class SharePointSiteListsRequest (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/admin/CopyStorageSourceRequest.java
  class CopyStorageSourceRequest (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/admin/UpdateStorageIdRequest.java
  class UpdateStorageIdRequest (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/admin/UpdateStorageSortRequest.java
  class UpdateStorageSortRequest (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/base/FileItemRequest.java
  class FileItemRequest (line 14) | @Data
    method handleDefaultValue (line 28) | public void handleDefaultValue() {

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/base/FileListConfigRequest.java
  class FileListConfigRequest (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/base/FileListRequest.java
  class FileListRequest (line 16) | @Data
    method handleDefaultValue (line 36) | public void handleDefaultValue() {

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/base/SaveStorageSourceRequest.java
  class SaveStorageSourceRequest (line 15) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/base/SearchStorageRequest.java
  class SearchStorageRequest (line 16) | @Data
    method getPathPasswordCache (line 45) | public String getPathPasswordCache(String path) {

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/operator/BatchDeleteRequest.java
  class BatchDeleteRequest (line 16) | @Data
    class DeleteItem (line 28) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/operator/BatchMoveOrCopyFileRequest.java
  class BatchMoveOrCopyFileRequest (line 16) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/operator/NewFolderRequest.java
  class NewFolderRequest (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/operator/RenameFileRequest.java
  class RenameFileRequest (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/operator/RenameFolderRequest.java
  class RenameFolderRequest (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/request/operator/UploadFileRequest.java
  class UploadFileRequest (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/FileInfoResult.java
  class FileInfoResult (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/FileItemResult.java
  class FileItemResult (line 17) | @Data
    method getFullPath (line 44) | @JsonIgnore

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/GoogleDriveInfoResult.java
  class GoogleDriveInfoResult (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/Open115AuthDeviceCodeResult.java
  class Open115AuthDeviceCodeResult (line 5) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/Open115GetStatusResult.java
  class Open115GetStatusResult (line 5) | @Data
    method error (line 18) | public static Open115GetStatusResult error(String msg) {
    method waiting (line 25) | public static Open115GetStatusResult waiting() {
    method scanning (line 31) | public static Open115GetStatusResult scanning(String msg) {
    method success (line 38) | public static Open115GetStatusResult success(String accessToken, Strin...

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/S3BucketNameResult.java
  class S3BucketNameResult (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/SharepointSiteListResult.java
  class SharepointSiteListResult (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/SharepointSiteResult.java
  class SharepointSiteResult (line 11) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/StorageSourceAdminResult.java
  class StorageSourceAdminResult (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/StorageSourceConfigResult.java
  class StorageSourceConfigResult (line 15) | @Schema(title="存储源设置响应类")

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/StorageSourceResult.java
  class StorageSourceResult (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/storage/model/result/operator/BatchOperatorResult.java
  class BatchOperatorResult (line 10) | @Data
    method success (line 21) | public static BatchOperatorResult success(String name, String path) {
    method fail (line 29) | public static BatchOperatorResult fail(String name, String path, Strin...

FILE: src/main/java/im/zhaojun/zfile/module/storage/oauth2/service/AbstractMicrosoftOAuth2Service.java
  class AbstractMicrosoftOAuth2Service (line 12) | @Slf4j
    method generateAuthorizationUrl (line 20) | @Override
    method getTokenByCode (line 36) | @Override
    method getEndPoint (line 73) | public abstract String getEndPoint();
    method getClientId (line 75) | public abstract String getClientId();
    method getClientSecret (line 77) | public abstract String getClientSecret();
    method getRedirectUri (line 79) | public abstract String getRedirectUri();
    method getScope (line 81) | public abstract String getScope();

FILE: src/main/java/im/zhaojun/zfile/module/storage/oauth2/service/GoogleDriveOAuth2ServiceImpl.java
  class GoogleDriveOAuth2ServiceImpl (line 21) | @Slf4j
    method generateAuthorizationUrl (line 30) | @Override
    method getTokenByCode (line 48) | @Override
    class NoRedirectClientHttpRequestFactory (line 85) | private static class NoRedirectClientHttpRequestFactory extends
      method prepareConnection (line 88) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/oauth2/service/IOAuth2Service.java
  type IOAuth2Service (line 5) | public interface IOAuth2Service {
    method generateAuthorizationUrl (line 23) | String generateAuthorizationUrl(String clientId, String clientSecret, ...
    method getTokenByCode (line 25) | OAuth2TokenDTO getTokenByCode(String code, String clientId, String cli...

FILE: src/main/java/im/zhaojun/zfile/module/storage/oauth2/service/OneDriveChinaOAuth2ServiceImpl.java
  class OneDriveChinaOAuth2ServiceImpl (line 8) | @Component
    method getEndPoint (line 14) | @Override
    method getClientId (line 19) | @Override
    method getClientSecret (line 24) | @Override
    method getRedirectUri (line 29) | @Override
    method getScope (line 34) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/oauth2/service/OneDriveOAuth2ServiceImpl.java
  class OneDriveOAuth2ServiceImpl (line 8) | @Component
    method getEndPoint (line 14) | @Override
    method getClientId (line 19) | @Override
    method getClientSecret (line 24) | @Override
    method getRedirectUri (line 29) | @Override
    method getScope (line 34) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/StorageSourceConfigService.java
  class StorageSourceConfigService (line 35) | @Service
    method selectStorageConfigByStorageId (line 51) | @Cacheable(key = "#storageId", unless = "#result == null or #result.si...
    method findByStorageIdAndName (line 68) | public StorageSourceConfig findByStorageIdAndName(Integer storageId, S...
    method deleteByStorageId (line 84) | @CacheEvict(key = "#storageId", beforeInvocation = true)
    method onStorageSourceDelete (line 98) | @EventListener
    method saveBatch (line 121) | @Transactional(rollbackFor = Exception.class)
    method updateBatch (line 145) | @Transactional(rollbackFor = Exception.class)
    method storageSourceAllParamToConfigList (line 173) | public List<StorageSourceConfig> storageSourceAllParamToConfigList(Int...
    method onStorageSourceCopy (line 218) | @EventListener

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/StorageSourceService.java
  class StorageSourceService (line 57) | @Slf4j
    method findAllOrderByOrderNum (line 92) | public List<StorageSource> findAllOrderByOrderNum() {
    method findAllEnableOrderByOrderNum (line 102) | public List<StorageSource> findAllEnableOrderByOrderNum(Integer userId) {
    method findById (line 119) | @Cacheable(key = "#id", unless = "#result == null", condition = "#id !...
    method findByStorageKey (line 135) | @Cacheable(key = "#storageKey", unless = "#result == null", condition ...
    method clearCacheByStorageKey (line 147) | @CacheEvict(key = "#storageKey")
    method findIdByKey (line 159) | public Integer findIdByKey(String storageKey) {
    method findStorageKeyById (line 172) | public String findStorageKeyById(Integer id){
    method findStorageTypeById (line 185) | public StorageTypeEnum findStorageTypeById(Integer id) {
    method findDTOById (line 198) | @Cacheable(key = "'dto-' + #id", unless = "#result == null", condition...
    method existByStorageKey (line 223) | public boolean existByStorageKey(String storageKey) {
    method deleteById (line 234) | @Transactional(rollbackFor = Exception.class)
    method updateStorageSort (line 266) | @Transactional(rollbackFor = Exception.class)
    method updateById (line 279) | @Caching(evict = {
    method saveStorageSource (line 296) | @Transactional(rollbackFor = Exception.class)
    method saveOrUpdate (line 359) | @Caching(evict = {
    method getStorageConfigSource (line 387) | public StorageSourceConfigResult getStorageConfigSource(FileListConfig...
    method copy (line 431) | @Transactional(rollbackFor = Exception.class)

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractBaseFileService.java
  class AbstractBaseFileService (line 22) | @Slf4j
    method init (line 52) | public void init(String name, Integer storageId, P param) {
    method init (line 68) | public abstract void init();
    method testConnection (line 73) | public void testConnection() {
    method getStorageSimpleInfo (line 82) | protected String getStorageSimpleInfo() {
    method getStorageSourceMetadata (line 87) | public abstract StorageSourceMetadata getStorageSourceMetadata();
    method getCurrentUserBasePath (line 89) | public String getCurrentUserBasePath() {
    method destroy (line 109) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractMicrosoftDriveService.java
  class AbstractMicrosoftDriveService (line 52) | @Slf4j
    method init (line 103) | @Override
    method getRestTemplate (line 127) | public RestTemplate getRestTemplate() {
    method fileList (line 145) | @Override
    method getFileItem (line 188) | @Override
    method getOriginFileItem (line 198) | public FileItemResult getOriginFileItem(String pathAndName) {
    method getFileOriginInfo (line 206) | @Nullable
    method newFolder (line 214) | @Override
    method deleteFolder (line 237) | @Override
    method deleteFile (line 242) | @Override
    method renameFile (line 251) | @Override
    method renameFolder (line 262) | @Override
    method getUploadUrl (line 267) | @Override
    method getOneDriveUploadUrl (line 275) | private String getOneDriveUploadUrl(String path, String name) {
    method uploadFile (line 290) | @Override
    method getDownloadUrl (line 318) | @Override
    method downloadToStream (line 330) | @Override
    method copyFile (line 349) | @Override
    method copyFolder (line 376) | @Override
    method moveFile (line 381) | @Override
    method moveFolder (line 409) | @Override
    method getType (line 418) | public abstract String getType();
    method getGraphEndPoint (line 424) | public abstract String getGraphEndPoint();
    method getAuthenticateEndPoint (line 431) | public abstract String getAuthenticateEndPoint();
    method getClientId (line 437) | public abstract String getClientId();
    method getRedirectUri (line 443) | public abstract String getRedirectUri();
    method getClientSecret (line 449) | public abstract String getClientSecret();
    method getScope (line 455) | public abstract String getScope();
    method refreshAccessToken (line 461) | @Override
    method jsonToFileItem (line 494) | private FileItemResult jsonToFileItem(JSONObject jsonObject, String fo...
    method getAuthorizationHttpEntity (line 523) | private HttpEntity<Object> getAuthorizationHttpEntity() {
    method getAuthorizationHttpEntity (line 538) | private <T> HttpEntity<T> getAuthorizationHttpEntity(T body) {
    method getStorageSourceMetadata (line 545) | @Override
    method getAndRefreshToken (line 562) | private RefreshTokenInfoDTO getAndRefreshToken() {
    method destroy (line 599) | @Override
    method checkExpiredAndGetAccessToken (line 613) | private String checkExpiredAndGetAccessToken() {

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractOneDriveServiceBase.java
  class AbstractOneDriveServiceBase (line 12) | @Slf4j
    method getType (line 15) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractProxyTransferService.java
  class AbstractProxyTransferService (line 20) | public abstract class AbstractProxyTransferService<P extends ProxyTransf...
    method getProxyDownloadUrl (line 50) | public String getProxyDownloadUrl(String pathAndName) {
    method getProxyDownloadUrl (line 66) | public String getProxyDownloadUrl(String pathAndName, boolean useParam...
    method getProxyUploadUrl (line 109) | public String getProxyUploadUrl(String path, String name) {
    method uploadFile (line 144) | public abstract void uploadFile(String pathAndName, InputStream inputS...
    method downloadToStream (line 155) | public abstract ResponseEntity<org.springframework.core.io.Resource> d...
    method getSystemConfigService (line 157) | protected SystemConfigService getSystemConfigService() {

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractS3BaseFileService.java
  class AbstractS3BaseFileService (line 51) | @Slf4j
    method fileList (line 64) | @Override
    method getDownloadUrl (line 74) | @Override
    method processGeneratePresignedUrlRequest (line 117) | public Consumer<GetObjectRequest.Builder> processGeneratePresignedUrlR...
    method s3FileList (line 127) | public List<FileItemResult> s3FileList(String path) {
    method getFileItem (line 174) | @Override
    method newFolder (line 198) | @Override
    method deleteFile (line 211) | @Override
    method deleteFolder (line 221) | @Override
    method renameFile (line 226) | @Override
    method renameFolder (line 233) | @Override
    method getUploadUrl (line 238) | @Override
    method copyFile (line 268) | @Override
    method copyFolder (line 284) | @Override
    method moveFile (line 289) | @Override
    method moveFolder (line 296) | @Override
    method setUploadCors (line 301) | protected void setUploadCors() {
    method uploadFile (line 320) | @Override
    method downloadToStream (line 335) | @Override
    method getClientConfiguration (line 355) | public ClientOverrideConfiguration getClientConfiguration() {
    method getStorageSourceMetadata (line 361) | @Override
    method parseContentTypeByName (line 377) | private static String parseContentTypeByName(String pathAndName, Strin...
    method destroy (line 385) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/base/AbstractSharePointServiceBase.java
  class AbstractSharePointServiceBase (line 8) | public abstract class AbstractSharePointServiceBase<P extends SharePoint...
    method getType (line 10) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/base/BaseFileService.java
  type BaseFileService (line 14) | public interface BaseFileService {
    method fileList (line 26) | List<FileItemResult> fileList(String folderPath) throws Exception;
    method getFileItem (line 36) | @Nullable
    method newFolder (line 50) | boolean newFolder(String path, String name);
    method deleteFile (line 63) | boolean deleteFile(String path, String name);
    method deleteFolder (line 76) | boolean deleteFolder(String path, String name);
    method copyFile (line 95) | boolean copyFile(String path, String name, String targetPath, String t...
    method copyFolder (line 114) | boolean copyFolder(String path, String name, String targetPath, String...
    method moveFile (line 133) | boolean moveFile(String path, String name, String targetPath, String t...
    method moveFolder (line 152) | boolean moveFolder(String path, String name, String targetPath, String...
    method renameFile (line 168) | boolean renameFile(String path, String name, String newName);
    method renameFolder (line 184) | boolean renameFolder(String path, String name, String newName);
    method getUploadUrl (line 200) | String getUploadUrl(String path, String name, Long size);
    method getDownloadUrl (line 210) | String getDownloadUrl(String pathAndName);
    method getStorageTypeEnum (line 217) | StorageTypeEnum getStorageTypeEnum();
    method destroy (line 222) | void destroy();

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/base/RefreshTokenService.java
  type RefreshTokenService (line 8) | public interface RefreshTokenService extends BaseFileService {
    method refreshAccessToken (line 15) | void refreshAccessToken() throws Exception;

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/AliyunServiceImpl.java
  class AliyunServiceImpl (line 36) | @Service
    method init (line 43) | @Override
    method getStorageTypeEnum (line 73) | @Override
    method getDownloadUrl (line 78) | @Override
    class Signer (line 108) | static class Signer {
      method Signer (line 116) | public Signer(String accessKey, String secretKey, String endPoint) {
      method sign (line 122) | private String sign(String data) {
      method generatePresignedUrl (line 153) | private String generatePresignedUrl(String bucketName, String key, H...

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/DogeCloudServiceImpl.java
  class DogeCloudServiceImpl (line 32) | @Service
    method init (line 39) | @Override
    method getStorageTypeEnum (line 59) | @Override
    method refreshAccessToken (line 64) | @Override
    method getToken (line 124) | private String getToken(String apiPath, String paramsText) {
    method checkExpiredAndGetAwsCredentials (line 132) | private AwsCredentials checkExpiredAndGetAwsCredentials() {

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/FtpServiceImpl.java
  class FtpServiceImpl (line 38) | @Service
    method init (line 49) | @Override
    method getClientFromPool (line 59) | public Ftp getClientFromPool() {
    method fileList (line 69) | @Override
    method getFileItem (line 95) | @Override
    method newFolder (line 124) | @Override
    method deleteFile (line 139) | @Override
    method deleteFolder (line 154) | @Override
    method renameFile (line 169) | @Override
    method renameFolder (line 175) | @Override
    method getDownloadUrl (line 181) | @Override
    method downloadToStream (line 191) | @Override
    method getUploadUrl (line 216) | @Override
    method uploadFile (line 222) | @Override
    method copyFile (line 239) | @Override
    method copyFolder (line 244) | @Override
    method moveFile (line 249) | @Override
    method moveFolder (line 267) | @Override
    method ftpFileToFileItem (line 272) | private FileItemResult ftpFileToFileItem(FTPFile ftpFile, String folde...
    method getStorageSourceMetadata (line 286) | @Override
    method getStorageTypeEnum (line 293) | public StorageTypeEnum getStorageTypeEnum() {
    method destroy (line 297) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/GoogleDriveServiceImpl.java
  class GoogleDriveServiceImpl (line 70) | @Service
    method init (line 104) | @Override
    method getIdByPath (line 115) | private String getIdByPath(String path) {
    method getIdByPath (line 127) | private String getIdByPath(String path, boolean concatCurrentUserBaseP...
    method fileList (line 164) | @Override
    method getFileItem (line 190) | @Override
    method newFolder (line 211) | @Override
    method deleteFile (line 226) | @Override
    method deleteFolder (line 237) | @Override
    method renameFile (line 242) | @Override
    method renameFolder (line 258) | @Override
    method getUploadUrl (line 264) | @Override
    method uploadFile (line 270) | @Override
    method getDownloadUrl (line 300) | @Override
    method downloadToStream (line 305) | @Override
    method copyFile (line 336) | @Override
    method copyFolder (line 354) | @Override
    method moveFile (line 359) | @Override
    method moveFolder (line 381) | @Override
    method getStorageTypeEnum (line 386) | @Override
    method getRefreshToken (line 396) | public RefreshTokenInfoDTO getRefreshToken() {
    method refreshAccessToken (line 435) | @Override
    method jsonArrayToFileList (line 470) | public List<FileItemResult> jsonArrayToFileList(JSONArray jsonArray, S...
    method jsonObjectToFileItem (line 492) | public FileItemResult jsonObjectToFileItem(JSONObject jsonObject, Stri...
    class GoogleDriveAPIParam (line 524) | @Data
      method getDriveIdByPathParam (line 559) | public String getDriveIdByPathParam(String folderPath, String parent...
      method getFileListParam (line 586) | public String getFileListParam(String folderId, String pageToken) {
      method getSearchParam (line 609) | public String getSearchParam(String folderId, String pageToken, Stri...
      method getBasicParam (line 628) | public GoogleDriveAPIParam getBasicParam() {
      method toString (line 651) | @Override
      method toString (line 661) | public String toString(boolean encodeValue) {
    method checkHttpResponseIsError (line 692) | private void checkHttpResponseIsError(HttpResponse httpResponse) {
    method checkHttpResponseIsError (line 707) | private void checkHttpResponseIsError(CloseableHttpResponse closeableH...
    method getStorageSourceMetadata (line 717) | @Override
    method commonHttpRequest (line 724) | private HttpRequest commonHttpRequest(HttpRequest httpRequest) {
    method checkExpiredAndGetAccessToken (line 733) | private String checkExpiredAndGetAccessToken() {

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/HuaweiServiceImpl.java
  class HuaweiServiceImpl (line 23) | @Service
    method init (line 28) | @Override
    method getStorageTypeEnum (line 57) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/LocalServiceImpl.java
  class LocalServiceImpl (line 45) | @Service
    method init (line 50) | @Override
    method fileList (line 62) | @Override
    method getFileItem (line 89) | @Override
    method newFolder (line 106) | @Override
    method deleteFile (line 116) | @Override
    method deleteFolder (line 126) | @Override
    method renameFile (line 132) | @Override
    method renameFolder (line 138) | @Override
    method getStorageTypeEnum (line 144) | @Override
    method uploadFile (line 150) | @Override
    method copyFile (line 169) | @Override
    method copyFolder (line 174) | @Override
    method moveFile (line 179) | @Override
    method moveFolder (line 184) | @Override
    method operateFile (line 189) | private boolean operateFile(String path, String name, String newPath, ...
    method getUploadUrl (line 219) | @Override
    method getDownloadUrl (line 225) | @Override
    method downloadToStream (line 234) | @Override
    method fileToFileItem (line 270) | private FileItemResult fileToFileItem(File file, String folderPath) {
    method checkPathSecurity (line 296) | private static void checkPathSecurity(String... paths) {
    method checkNameSecurity (line 315) | private static void checkNameSecurity(String... names) {
    method getStorageSourceMetadata (line 324) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/MinIOServiceImpl.java
  class MinIOServiceImpl (line 24) | @Service
    method init (line 29) | @Override
    method getStorageTypeEnum (line 63) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/OneDriveChinaServiceImpl.java
  class OneDriveChinaServiceImpl (line 17) | @Service
    method getStorageTypeEnum (line 25) | @Override
    method getGraphEndPoint (line 30) | @Override
    method getAuthenticateEndPoint (line 35) | @Override
    method getClientId (line 40) | @Override
    method getRedirectUri (line 48) | @Override
    method getClientSecret (line 56) | @Override
    method getScope (line 64) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/OneDriveServiceImpl.java
  class OneDriveServiceImpl (line 17) | @Service
    method getStorageTypeEnum (line 25) | @Override
    method getGraphEndPoint (line 30) | @Override
    method getAuthenticateEndPoint (line 35) | @Override
    method getClientId (line 40) | @Override
    method getRedirectUri (line 48) | @Override
    method getClientSecret (line 56) | @Override
    method getScope (line 64) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/Open115ServiceImpl.java
  class Open115ServiceImpl (line 52) | @Service
    method init (line 105) | @Override
    method fileList (line 119) | @Override
    method getFileItem (line 156) | @Override
    method newFolder (line 172) | @Override
    method deleteFile (line 191) | @Override
    method deleteFolder (line 208) | @Override
    method copyFile (line 225) | @Override
    method copyFolder (line 241) | @Override
    method moveFile (line 258) | @Override
    method moveFolder (line 276) | @Override
    method renameFile (line 292) | @Override
    method renameFolder (line 310) | @Override
    method getDownloadUrl (line 328) | @Override
    method getProxyDownloadUrlByPickCode (line 341) | private String getProxyDownloadUrlByPickCode(String pathAndName, Strin...
    method getOpen115DownloadUrlByPickCode (line 349) | public String getOpen115DownloadUrlByPickCode(String pickCode) {
    method getUploadUrl (line 390) | @Override
    method uploadFile (line 395) | @Override
    method downloadToStream (line 416) | @Override
    method refreshAccessToken (line 455) | @Override
    method getStorageSourceMetadata (line 477) | @Override
    method getStorageTypeEnum (line 484) | @Override
    method getAndRefreshToken (line 494) | private RefreshTokenInfoDTO getAndRefreshToken() {
    method checkExpiredAndGetAccessToken (line 515) | private String checkExpiredAndGetAccessToken() {
    method sendRequest (line 538) | private JSONObject sendRequest(String url, Method method, boolean with...
    method sendPostRequestWithAuth (line 553) | private JSONObject sendPostRequestWithAuth(String url, Map<String, Obj...
    method sendGetRequestWithAuth (line 557) | private JSONObject sendGetRequestWithAuth(String url, Map<String, Obje...
    method handleEntity (line 561) | private static JSONObject handleEntity(HttpResponse httpResponse) {
    method listJsonToFileItem (line 587) | private FileItemResult listJsonToFileItem(JSONObject jsonObject, Strin...
    method itemJsonToFileItem (line 615) | private FileItemResult itemJsonToFileItem(JSONObject jsonObject, Strin...

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/QiniuServiceImpl.java
  class QiniuServiceImpl (line 29) | @Service
    method init (line 38) | @Override
    method getStorageTypeEnum (line 72) | @Override
    method renameFile (line 77) | @Override
    method getDownloadUrl (line 97) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/S3ServiceImpl.java
  class S3ServiceImpl (line 25) | @Service
    method init (line 30) | @Override
    method getStorageTypeEnum (line 83) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/SftpServiceImpl.java
  class SftpServiceImpl (line 40) | @Service
    method init (line 47) | @Override
    method getClientFromPool (line 60) | public Sftp getClientFromPool() {
    method fileList (line 70) | @Override
    method getStorageTypeEnum (line 92) | @Override
    method getFileItem (line 98) | public FileItemResult getFileItem(String pathAndName, boolean containU...
    method getFileItem (line 123) | @Override
    method newFolder (line 128) | @Override
    method deleteFile (line 143) | @Override
    method deleteFolder (line 157) | @Override
    method copyFile (line 170) | @Override
    method copyFolder (line 175) | @Override
    method moveFile (line 180) | @Override
    method moveFolder (line 198) | @Override
    method renameFile (line 204) | @Override
    method renameFolder (line 223) | @Override
    method downloadToStream (line 229) | @Override
    method getUploadUrl (line 266) | @Override
    method getDownloadUrl (line 272) | @Override
    method uploadFile (line 280) | @Override
    method sftpEntryToFileItem (line 297) | public FileItemResult sftpEntryToFileItem(ChannelSftp.LsEntry sftpEntr...
    method getStorageSourceMetadata (line 310) | @Override
    method destroy (line 317) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/SharePointChinaServiceImpl.java
  class SharePointChinaServiceImpl (line 17) | @Service
    method getStorageTypeEnum (line 25) | @Override
    method getGraphEndPoint (line 30) | @Override
    method getAuthenticateEndPoint (line 35) | @Override
    method getClientId (line 40) | @Override
    method getRedirectUri (line 48) | @Override
    method getClientSecret (line 56) | @Override
    method getScope (line 64) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/SharePointServiceImpl.java
  class SharePointServiceImpl (line 17) | @Service
    method getStorageTypeEnum (line 25) | @Override
    method getGraphEndPoint (line 30) | @Override
    method getAuthenticateEndPoint (line 35) | @Override
    method getClientId (line 40) | @Override
    method getRedirectUri (line 48) | @Override
    method getClientSecret (line 56) | @Override
    method getScope (line 64) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/TencentServiceImpl.java
  class TencentServiceImpl (line 28) | @Service
    method init (line 33) | @Override
    method getStorageTypeEnum (line 62) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/UpYunServiceImpl.java
  class UpYunServiceImpl (line 45) | @Service
    method init (line 62) | @Override
    method fileList (line 70) | @Override
    method getDownloadUrl (line 101) | @Override
    method getOriginDownloadUrl (line 110) | public String getOriginDownloadUrl(String pathAndName) {
    method getStorageTypeEnum (line 128) | @Override
    method getFileItem (line 133) | @Override
    method newFolder (line 164) | @Override
    method deleteFile (line 174) | @Override
    method deleteFolder (line 190) | @Override
    method renameFile (line 195) | @Override
    method renameFolder (line 207) | @Override
    method getUploadUrl (line 212) | @Override
    method copyFile (line 225) | @Override
    method copyFolder (line 237) | @Override
    method moveFile (line 242) | @Override
    method moveFolder (line 254) | @Override
    method sign (line 262) | private String sign(String key, String secret, String method, String u...
    method generatorAuthModel (line 273) | public AuthModel generatorAuthModel(UploadSignParam uploadSignParam) {
    method getPolicy (line 293) | private String getPolicy(UploadSignParam uploadSignParam) {
    method uploadFile (line 307) | @Override
    method tryUpload (line 328) | private void tryUpload(String pathAndName, InputStream inputStream) th...
    method downloadToStream (line 337) | @Override
    method getStorageSourceMetadata (line 348) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/service/impl/WebdavServiceImpl.java
  class WebdavServiceImpl (line 38) | @Service
    method getRequestPath (line 47) | private String getRequestPath(String... strs) {
    method getRequestPath (line 51) | private String getRequestPath(boolean containUserBasePath, String... s...
    method init (line 58) | @SneakyThrows
    method fileList (line 66) | @Override
    method getStorageTypeEnum (line 87) | @Override
    method getFileItem (line 93) | @Override
    method getFileItem (line 98) | public FileItemResult getFileItem(String pathAndName, boolean containU...
    method newFolder (line 120) | @Override
    method deleteFile (line 131) | @Override
    method deleteFolder (line 141) | @Override
    method renameFolder (line 146) | @Override
    method renameFile (line 151) | @Override
    method getDownloadUrl (line 157) | @Override
    method downloadToStream (line 169) | @Override
    method getUploadUrl (line 177) | @Override
    method uploadFile (line 182) | @Override
    method davResourceToFileItem (line 192) | private FileItemResult davResourceToFileItem(DavResource davResource, ...
    method copyFile (line 205) | @Override
    method copyFolder (line 210) | @Override
    method moveFile (line 220) | @Override
    method moveFolder (line 225) | @Override
    method getStorageSourceMetadata (line 235) | @Override
    method destroy (line 243) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/support/Open115IdCacheService.java
  class Open115IdCacheService (line 23) | @Slf4j
    method Open115IdCacheService (line 58) | public Open115IdCacheService(BiFunction<String, Map<String, Object>, J...
    method getFileId (line 69) | public String getFileId(String fullPath, boolean throwIfNotFound) {
    method getPathId (line 96) | public String getPathId(String fullPath, boolean throwIfNotFound) {
    method cachePathAndFileId (line 122) | private void cachePathAndFileId(String folderPath) {
    method putFileId (line 162) | public void putFileId(String fullPath, String id) {
    method putPathId (line 166) | public void putPathId(String fullPath, String id) {
    method deleteFileId (line 170) | public void deleteFileId(String fullPath) {
    method deletePathId (line 174) | public void deletePathId(String fullPath) {
    method removeFileIdByPath (line 182) | public String removeFileIdByPath(String fullPath) {

FILE: src/main/java/im/zhaojun/zfile/module/storage/support/StorageSourceSupport.java
  class StorageSourceSupport (line 27) | public class StorageSourceSupport {
    method getStorageSourceParamList (line 39) | public static List<StorageSourceParamDef> getStorageSourceParamList(Ab...
    method getOptionsList (line 180) | private static List<StorageSourceParamDef.Options> getOptionsList(Stor...
    method parseAnnotationLinkField (line 212) | private static String parseAnnotationLinkField(StorageParamItem storag...

FILE: src/main/java/im/zhaojun/zfile/module/storage/support/ftp/FtpClientFactory.java
  class FtpClientFactory (line 16) | @Slf4j
    method FtpClientFactory (line 27) | public FtpClientFactory(String host, int port, String username, String...
    method create (line 36) | @Override
    method wrap (line 47) | @Override
    method validateObject (line 52) | @Override
    method destroyObject (line 65) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/support/ftp/FtpClientPool.java
  class FtpClientPool (line 8) | public class FtpClientPool extends GenericObjectPool<Ftp> {
    method FtpClientPool (line 10) | public FtpClientPool(PooledObjectFactory<Ftp> factory) {
    method FtpClientPool (line 14) | public FtpClientPool(PooledObjectFactory<Ftp> factory, GenericObjectPo...

FILE: src/main/java/im/zhaojun/zfile/module/storage/support/sftp/SFtpClientFactory.java
  class SFtpClientFactory (line 16) | @Slf4j
    method SFtpClientFactory (line 32) | public SFtpClientFactory(String host, int port, String username, Strin...
    method create (line 42) | @Override
    method wrap (line 62) | @Override
    method validateObject (line 67) | @Override
    method destroyObject (line 80) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/storage/support/sftp/SFtpClientPool.java
  class SFtpClientPool (line 8) | public class SFtpClientPool extends GenericObjectPool<Sftp> {
    method SFtpClientPool (line 10) | public SFtpClientPool(PooledObjectFactory<Sftp> factory) {
    method SFtpClientPool (line 14) | public SFtpClientPool(PooledObjectFactory<Sftp> factory, GenericObject...

FILE: src/main/java/im/zhaojun/zfile/module/storage/support/webdav/CustomSardine.java
  class CustomSardine (line 15) | public class CustomSardine extends SardineImpl {
    method CustomSardine (line 21) | public CustomSardine(String username, String password, Duration connec...
    method execute (line 28) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/user/aspect/LoginLogAspect.java
  class LoginLogAspect (line 21) | @Aspect
    method around (line 40) | @Around(value = "execution(* im.zhaojun.zfile.module.user.controller.U...

FILE: src/main/java/im/zhaojun/zfile/module/user/controller/AdminTwoFAController.java
  class AdminTwoFAController (line 23) | @Tag(name = "登录模块")
    method setupDevice (line 32) | @ApiOperationSupport(order = 1)
    method deviceVerify (line 41) | @ApiOperationSupport(order = 2)

FILE: src/main/java/im/zhaojun/zfile/module/user/controller/UserController.java
  class UserController (line 44) | @Slf4j
    method initSecureLoginEntry (line 66) | @EventListener(ApplicationReadyEvent.class)
    method validateLoginEntry (line 79) | @ApiOperationSupport(order = 0)
    method doLogin (line 91) | @ApiOperationSupport(order = 1, ignoreParameters = {"zfile-token"})
    method logout (line 109) | @ApiOperationSupport(order = 2)
    method loginVerifyMode (line 117) | @ApiOperationSupport(order = 3)
    method captcha (line 141) | @ApiOperationSupport(order = 4)
    method checkLogin (line 150) | @ApiOperationSupport(order = 5)
    method updatePwd (line 168) | @SaCheckLogin
    method resetPwd (line 179) | @ResponseBody

FILE: src/main/java/im/zhaojun/zfile/module/user/controller/UserManagerController.java
  class UserManagerController (line 28) | @Tag(name = "用户管理")
    method list (line 37) | @ApiOperationSupport(order = 1)
    method saveOrUpdate (line 46) | @ApiOperationSupport(order = 2)
    method delete (line 55) | @ApiOperationSupport(order = 3)
    method enable (line 65) | @ApiOperationSupport(order = 5)
    method disable (line 75) | @ApiOperationSupport(order = 6)
    method getUser (line 85) | @ApiOperationSupport(order = 7)
    method checkDuplicate (line 94) | @ApiOperationSupport(order = 8)
    method copyStorage (line 104) | @ApiOperationSupport(order = 9)

FILE: src/main/java/im/zhaojun/zfile/module/user/event/UserCopyEvent.java
  class UserCopyEvent (line 10) | @Data
    method UserCopyEvent (line 17) | public UserCopyEvent(Integer fromId, Integer newId) {

FILE: src/main/java/im/zhaojun/zfile/module/user/event/UserDeleteEvent.java
  class UserDeleteEvent (line 11) | @Data
    method UserDeleteEvent (line 18) | public UserDeleteEvent(User user) {

FILE: src/main/java/im/zhaojun/zfile/module/user/manager/UserManager.java
  class UserManager (line 22) | @Slf4j
    method saveUserInfo (line 42) | @Caching(evict = {
    method deleteAllByUserId (line 71) | @Transactional(rollbackFor = Exception.class)
    method assembleUserDetail (line 89) | public UserDetailResponse assembleUserDetail(User user) {
    method addDefaultPermissionsForAllUsersInStorageSource (line 112) | @Transactional(rollbackFor = Exception.class)

FILE: src/main/java/im/zhaojun/zfile/module/user/mapper/UserMapper.java
  type UserMapper (line 8) | @Mapper
    method findIdByUsername (line 11) | Integer findIdByUsername(@Param("username") String username);
    method countByUsername (line 13) | int countByUsername(@Param("username") String username, @Param("ignore...
    method updateUserNameAndPwdById (line 15) | int updateUserNameAndPwdById(@Param("id") Integer id, @Param("username...

FILE: src/main/java/im/zhaojun/zfile/module/user/mapper/UserStorageSourceMapper.java
  type UserStorageSourceMapper (line 11) | @Mapper
    method deleteByUserId (line 14) | int deleteByUserId(@Param("userId") Integer userId);
    method deleteByStorageId (line 16) | int deleteByStorageId(@Param("storageId") Integer storageId);
    method getDTOListByUserId (line 18) | List<UserStorageSourceDetailDTO> getDTOListByUserId(@Param("userId") I...
    method getByUserIdAndStorageId (line 20) | UserStorageSource getByUserIdAndStorageId(@Param("userId") Integer use...
    method selectByStorageId (line 22) | List<UserStorageSource> selectByStorageId(@Param("storageId") Integer ...
    method selectByUserId (line 24) | List<UserStorageSource> selectByUserId(@Param("userId") Integer userId);

FILE: src/main/java/im/zhaojun/zfile/module/user/model/constant/UserConstant.java
  class UserConstant (line 3) | public class UserConstant {

FILE: src/main/java/im/zhaojun/zfile/module/user/model/dto/UserStorageSourceDetailDTO.java
  class UserStorageSourceDetailDTO (line 16) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/entity/User.java
  class User (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/entity/UserStorageSource.java
  class UserStorageSource (line 12) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/enums/LoginLogModeEnum.java
  type LoginLogModeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/user/model/enums/LoginVerifyModeEnum.java
  type LoginVerifyModeEnum (line 13) | @Getter

FILE: src/main/java/im/zhaojun/zfile/module/user/model/request/CheckUserDuplicateRequest.java
  class CheckUserDuplicateRequest (line 6) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/request/CopyUserRequest.java
  class CopyUserRequest (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/request/QueryUserRequest.java
  class QueryUserRequest (line 14) | @Data
    method getDateFrom (line 39) | public Date getDateFrom() {
    method getDateTo (line 46) | public Date getDateTo() {

FILE: src/main/java/im/zhaojun/zfile/module/user/model/request/ResetAdminUserNameAndPasswordRequest.java
  class ResetAdminUserNameAndPasswordRequest (line 11) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/request/SaveUserRequest.java
  class SaveUserRequest (line 10) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/request/UpdateUserPwdRequest.java
  class UpdateUserPwdRequest (line 7) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/request/UserLoginRequest.java
  class UserLoginRequest (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/request/VerifyLoginTwoFactorAuthenticatorRequest.java
  class VerifyLoginTwoFactorAuthenticatorRequest (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/response/UserDetailResponse.java
  class UserDetailResponse (line 11) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/result/CheckLoginResult.java
  class CheckLoginResult (line 7) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/result/LoginResult.java
  class LoginResult (line 7) | @Data
    method LoginResult (line 16) | public LoginResult(String tokenValue, boolean isAdmin) {

FILE: src/main/java/im/zhaojun/zfile/module/user/model/result/LoginTwoFactorAuthenticatorResult.java
  class LoginTwoFactorAuthenticatorResult (line 14) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/model/result/LoginVerifyImgResult.java
  class LoginVerifyImgResult (line 13) | @Data

FILE: src/main/java/im/zhaojun/zfile/module/user/service/DynamicLoginEntryService.java
  class DynamicLoginEntryService (line 22) | @Slf4j
    method registerMappingHandlerMapping (line 31) | public void registerMappingHandlerMapping(String key, RequestMappingIn...
    method updateRegisterMappingHandler (line 36) | public void updateRegisterMappingHandler(String key, RequestMappingInf...
    method buildLoginRequestMappingInfo (line 49) | public RequestMappingInfo buildLoginRequestMappingInfo(String secureLo...

FILE: src/main/java/im/zhaojun/zfile/module/user/service/UserService.java
  class UserService (line 37) | @Slf4j
    method getById (line 64) | @Cacheable(key = "#id", unless = "#result == null", condition = "#id !...
    method getIdByUsername (line 69) | @Cacheable(key = "#username", unless = "#result == null", condition = ...
    method getByUsername (line 82) | public User getByUsername(String username) {
    method getUserDetailById (line 100) | public UserDetailResponse getUserDetailById(Integer userId) {
    method listUserDetail (line 117) | public List<UserDetailResponse> listUserDetail(QueryUserRequest queryO...
    method saveOrUpdate (line 149) | @CacheEvict(allEntries = true)
    method updateUserEnable (line 184) | @CacheEvict(key = "#id")
    method initAdminUser (line 205) | public boolean initAdminUser(String username, String password) {
    method updateUserNameAndPwdById (line 232) | @CacheEvict(allEntries = true)
    method deleteById (line 262) | @CacheEvict(allEntries = true)
    method isAdmin (line 289) | public boolean isAdmin(Integer id) {
    method isAdmin (line 302) | public boolean isAdmin(String username) {
    method checkDuplicateUsername (line 319) | public boolean checkDuplicateUsername(Integer ignoreUserId, String use...
    method resetAdminLoginInfo (line 329) | @CacheEvict(allEntries = true)
    method passwordEncryptAndSet (line 348) | private static void passwordEncryptAndSet(String originPassword, User ...
    method copy (line 354) | @Transactional(rollbackFor = Exception.class)

FILE: src/main/java/im/zhaojun/zfile/module/user/service/UserStorageSourceService.java
  class UserStorageSourceService (line 27) | @Slf4j
    method getByUserIdAndStorageId (line 52) | @Cacheable(key = "#userId + '-' + #storageId",
    method hasCurrentUserStorageOperatorPermission (line 70) | public boolean hasCurrentUserStorageOperatorPermission(Integer storage...
    method hasUserStorageOperatorPermission (line 78) | public boolean hasUserStorageOperatorPermission(Integer userId, Intege...
    method getCurrentUserPermissionMapByStorageId (line 98) | public HashMap<String, Boolean> getCurrentUserPermissionMapByStorageId...
    method getPermissionMapByUserIdAndStorageId (line 108) | public HashMap<String, Boolean> getPermissionMapByUserIdAndStorageId(I...
    method buildPermissionMap (line 117) | private HashMap<String, Boolean> buildPermissionMap(UserStorageSource ...
    method addDefaultPermissionsForAllUsersInStorageSource (line 133) | public void addDefaultPermissionsForAllUsersInStorageSource(Integer st...
    method deleteByStorageId (line 146) | @CacheEvict(allEntries = true)
    method onStorageSourceDelete (line 159) | @EventListener
    method onStorageSourceCopy (line 177) | @EventListener
    method onUserCopy (line 202) | @EventListener

FILE: src/main/java/im/zhaojun/zfile/module/user/service/login/ImgVerifyCodeService.java
  class ImgVerifyCodeService (line 21) | @Service
    method generatorCaptcha (line 36) | public LoginVerifyImgResult generatorCaptcha() {
    method verifyCaptcha (line 69) | public boolean verifyCaptcha(String uuid, String code) {
    method checkCaptcha (line 84) | public void checkCaptcha(String uuid, String code) {

FILE: src/main/java/im/zhaojun/zfile/module/user/service/login/LoginService.java
  class LoginService (line 11) | @Slf4j
    method verify (line 18) | public void verify(UserLoginRequest userLoginRequest) {

FILE: src/main/java/im/zhaojun/zfile/module/user/service/login/TwoFactorAuthenticatorVerifyService.java
  class TwoFactorAuthenticatorVerifyService (line 23) | @Service
    method setupDevice (line 44) | public LoginTwoFactorAuthenticatorResult setupDevice() {
    method deviceVerify (line 62) | public void deviceVerify(VerifyLoginTwoFactorAuthenticatorRequest veri...
    method checkCode (line 87) | public void checkCode(String loginVerifySecret, String verifyCode) {

FILE: src/main/java/im/zhaojun/zfile/module/user/service/login/verify/LoginVerifyService.java
  type LoginVerifyService (line 5) | public interface LoginVerifyService {
    method verify (line 7) | void verify(UserLoginRequest userLoginRequest);

FILE: src/main/java/im/zhaojun/zfile/module/user/service/login/verify/impl/ImgCodeLoginVerifyService.java
  class ImgCodeLoginVerifyService (line 16) | @Slf4j
    method verify (line 30) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/user/service/login/verify/impl/PasswordVerifyService.java
  class PasswordVerifyService (line 17) | @Slf4j
    method verify (line 25) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/user/service/login/verify/impl/TwoFactorAuthLoginVerifyService.java
  class TwoFactorAuthLoginVerifyService (line 16) | @Slf4j
    method verify (line 30) | @Override

FILE: src/main/java/im/zhaojun/zfile/module/user/util/LoginEntryPathUtils.java
  class LoginEntryPathUtils (line 10) | public final class LoginEntryPathUtils {
    method LoginEntryPathUtils (line 12) | private LoginEntryPathUtils() {
    method resolveLoginPath (line 24) | public static String resolveLoginPath(String secureLoginEntry) {
    method resolveLoginApiPath (line 38) | public static String resolveLoginApiPath(String secureLoginEntry) {

FILE: src/main/java/im/zhaojun/zfile/module/user/utils/PasswordVerifyUtils.java
  class PasswordVerifyUtils (line 12) | public class PasswordVerifyUtils {
    method verify (line 14) | public static boolean verify(String dbPassword, String dbSalt, String ...
    method encrypt (line 26) | public static Pair<String, String> encrypt(String password) {

FILE: src/main/resources/db/migration-mysql/V1__Base_version.sql
  type storage_source (line 1) | create table if not exists storage_source
(
    id int auto_increment
  ...
  type filter_config (line 1) | create table if not exists filter_config
(
    id int auto_increment
   ...
  type short_link (line 1) | create table if not exists short_link
(
    id int auto_increment
      ...
  type storage_source_config (line 1) | create table if not exists storage_source_config
(
    id int auto_incre...
  type system_config (line 1) | create table if not exists system_config
(
    id int auto_increment
   ...
  type password_config (line 1) | create table if not exists password_config
(
    id int auto_increment
 ...
  type readme_config (line 1) | create table if not exists readme_config
(
    id int auto_increment
   ...
  type download_log (line 1) | create table if not exists download_log
(
    id   int auto_increment pr...

FILE: src/main/resources/db/migration-mysql/V27__add_table_login_log.sql
  type login_log (line 1) | create table if not exists login_log

FILE: src/main/resources/db/migration-mysql/V28__add_multi_user.sql
  type user (line 1) | CREATE TABLE IF NOT EXISTS user
  type user_storage_source (line 13) | CREATE TABLE IF NOT EXISTS user_storage_source

FILE: src/main/resources/db/migration-mysql/V45__add_sso_config.sql
  type `sso_config` (line 1) | CREATE TABLE IF NOT EXISTS `sso_config`

FILE: src/main/resources/db/migration-mysql/V54__add_share_link_table.sql
  type `share_link` (line 1) | CREATE TABLE IF NOT EXISTS `share_link`

FILE: src/main/resources/db/migration-mysql/V5__add_permission_config_table.sql
  type permission_config (line 1) | create table if not exists permission_config

FILE: src/main/resources/db/migration-sqlite/V12__system_config_modify_field_value_to_text.sql
  type system_config_dg_tmp (line 1) | create table system_config_dg_tmp

FILE: src/main/resources/db/migration-sqlite/V1__Base_version.sql
  type storage_source (line 1) | create table if not exists storage_source
(
    id                      ...
  type filter_config (line 1) | create table if not exists filter_config
(
    id           integer
    ...
  type short_link (line 1) | create table if not exists short_link
(
    id          integer
        ...
  type storage_source_config (line 1) | create table if not exists storage_source_config
(
    id         intege...
  type system_config (line 1) | create table if not exists system_config
(
    id    integer
        pri...
  type password_config (line 1) | create table if not exists password_config
(
    id         integer
    ...
  type readme_config (line 1) | create table if not exists readme_config
(
    id         integer
      ...
  type download_log (line 1) | create table if not exists download_log
(
    id                        ...

FILE: src/main/resources/db/migration-sqlite/V27__add_table_login_log.sql
  type login_log (line 1) | create table if not exists login_log

FILE: src/main/resources/db/migration-sqlite/V28__add_multi_user.sql
  type user (line 1) | create table if not exists user
  type user_storage_source (line 13) | create table if not exists user_storage_source

FILE: src/main/resources/db/migration-sqlite/V2__download_log_modify_storage_key_field_length.sql
  type download_log_dg_tmp (line 1) | create table download_log_dg_tmp

FILE: src/main/resources/db/migration-sqlite/V45__add_sso_config.sql
  type sso_config (line 1) | create table if not exists sso_config

FILE: src/main/resources/db/migration-sqlite/V4__download_log_modify_ip_field_length.sql
  type download_log_dg_tmp (line 1) | create table download_log_dg_tmp

FILE: src/main/resources/db/migration-sqlite/V54__add_share_link_table.sql
  type share_link (line 1) | create table share_link
  type idx_share_key (line 17) | create unique index idx_share_key on share_link(share_key)

FILE: src/main/resources/db/migration-sqlite/V5__add_permission_config_table.sql
  type permission_config (line 1) | create table if not exists permission_config
Condensed preview — 565 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,271K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 322,
    "preview": "name: 'Blank Issue'\ndescription: 请使用 https://issue.zfile.vip 创建新的问题.\nbody:\n  - type: markdown\n    attributes:\n      valu"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 149,
    "preview": "blank_issues_enabled: true\ncontact_links:\n  - name: 创建 Issue\n    url: https://issue.zfile.vip/\n    about: 未通过 https://is"
  },
  {
    "path": ".gitignore",
    "chars": 459,
    "preview": "HELP.md\ntarget/\n!.mvn/wrapper/maven-wrapper.jar\n!**/src/main/**/target/\n!**/src/test/**/target/\n\n### STS ###\n.apt_genera"
  },
  {
    "path": ".package/script/log.sh",
    "chars": 50,
    "preview": "#!/bin/bash\ntail -fn100 ~/.zfile-v4/logs/zfile.log"
  },
  {
    "path": ".package/script/restart.sh",
    "chars": 96,
    "preview": "#!/bin/bash\n\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\"\n\n$DIR/stop.sh\n$DIR/start.sh"
  },
  {
    "path": ".package/script/start.sh",
    "chars": 603,
    "preview": "#!/bin/bash\n\n# 检测是否已启动\npid=`ps -ef | grep -n zfile | grep -v grep | grep -v launch | grep -v .sh | awk '{print $2}'`\nif "
  },
  {
    "path": ".package/script/status.sh",
    "chars": 287,
    "preview": "#!/bin/bash\n\necho \"------------------ 检测状态 START --------------\"\npid=`ps -ef | grep -n zfile | grep -v grep | grep -v la"
  },
  {
    "path": ".package/script/stop.sh",
    "chars": 312,
    "preview": "#!/bin/bash\n\necho \"------------------ 检测状态 START --------------\"\npid=`ps -ef | grep -n zfile | grep -v grep | grep -v .s"
  },
  {
    "path": ".package/script/双击我启动.bat",
    "chars": 131,
    "preview": "@echo off\nif not exist %windir%\\system32\\cmd.exe (\n    \"%CD%\\zfile\\zfile.exe\"\n) else (\n    cmd /k \"%CD%\\zfile\\zfile.exe\""
  },
  {
    "path": "Dockerfile",
    "chars": 653,
    "preview": "# 此文件仅作为示例使用,与 ZFile 实际打包的 Dockerfile 不同(采用 Graal Native 打包,这部分不开源)\nFROM maven:3.9.9-eclipse-temurin-21-alpine AS builde"
  },
  {
    "path": "LICENSE",
    "chars": 1064,
    "preview": "MIT License\n\nCopyright (c) 2019 ZhaoJun\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
  },
  {
    "path": "README.md",
    "chars": 3122,
    "preview": "<div align=\"center\">\n    <a href=\"https://zfile.vip\" target=\"_blank\" rel=\"noopener noreferrer\">\n        <img style=\"marg"
  },
  {
    "path": "pom.xml",
    "chars": 11687,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/ZfileApplication.java",
    "chars": 777,
    "preview": "package im.zhaojun.zfile;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfig"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/annotation/ApiLimit.java",
    "chars": 571,
    "preview": "package im.zhaojun.zfile.core.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retentio"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/annotation/DemoDisable.java",
    "chars": 347,
    "preview": "package im.zhaojun.zfile.core.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retentio"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/aspect/ApiLimitAspect.java",
    "chars": 2116,
    "preview": "package im.zhaojun.zfile.core.aspect;\n\nimport cn.hutool.cache.CacheUtil;\nimport cn.hutool.cache.impl.TimedCache;\nimport "
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/aspect/CommonResultControllerAdvice.java",
    "chars": 2564,
    "preview": "package im.zhaojun.zfile.core.aspect;\n\nimport im.zhaojun.zfile.core.constant.MdcConstant;\nimport im.zhaojun.zfile.core.u"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/aspect/DemoDisableAspect.java",
    "chars": 1150,
    "preview": "package im.zhaojun.zfile.core.aspect;\n\nimport im.zhaojun.zfile.core.annotation.DemoDisable;\nimport im.zhaojun.zfile.core"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/cache/ZFileCacheManager.java",
    "chars": 1361,
    "preview": "package im.zhaojun.zfile.core.cache;\n\nimport im.zhaojun.zfile.module.storage.model.entity.StorageSource;\nimport org.spri"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/ZFileProperties.java",
    "chars": 1024,
    "preview": "package im.zhaojun.zfile.core.config;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.Configurat"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/datasource/DataSourceBeanPostProcessor.java",
    "chars": 4450,
    "preview": "package im.zhaojun.zfile.core.config.datasource;\n\n\nimport cn.hutool.core.io.FileUtil;\nimport cn.hutool.extra.spring.Spri"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/docs/Knife4jConfiguration.java",
    "chars": 2463,
    "preview": "package im.zhaojun.zfile.core.config.docs;\n\nimport io.swagger.v3.oas.models.OpenAPI;\nimport io.swagger.v3.oas.models.inf"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/jackson/JSONStringDeserializer.java",
    "chars": 759,
    "preview": "package im.zhaojun.zfile.core.config.jackson;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackso"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/jackson/JSONStringSerializer.java",
    "chars": 552,
    "preview": "package im.zhaojun.zfile.core.config.jackson;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jac"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/mybatis/CollectionIntegerTypeHandler.java",
    "chars": 160,
    "preview": "package im.zhaojun.zfile.core.config.mybatis;\n\nimport java.util.Set;\n\npublic class CollectionIntegerTypeHandler extends "
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/mybatis/CollectionStrTypeHandler.java",
    "chars": 155,
    "preview": "package im.zhaojun.zfile.core.config.mybatis;\n\nimport java.util.Set;\n\npublic class CollectionStrTypeHandler extends Coll"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/mybatis/CollectionTypeHandler.java",
    "chars": 3787,
    "preview": "package im.zhaojun.zfile.core.config.mybatis;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.util.NumberU"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/mybatis/MyBatisPlusConfig.java",
    "chars": 1044,
    "preview": "package im.zhaojun.zfile.core.config.mybatis;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.my"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/mybatis/MyDatabaseIdProvider.java",
    "chars": 1227,
    "preview": "package im.zhaojun.zfile.core.config.mybatis;\n\nimport org.apache.ibatis.mapping.DatabaseIdProvider;\nimport org.springfra"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/mybatis/MyMetaObjectHandler.java",
    "chars": 750,
    "preview": "package im.zhaojun.zfile.core.config.mybatis;\n\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport l"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/security/SaSessionForJacksonCustomized.java",
    "chars": 655,
    "preview": "package im.zhaojun.zfile.core.config.security;\n\nimport cn.dev33.satoken.session.SaSession;\nimport com.fasterxml.jackson."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/security/SaTokenConfigure.java",
    "chars": 1180,
    "preview": "package im.zhaojun.zfile.core.config.security;\n\nimport cn.dev33.satoken.interceptor.SaInterceptor;\nimport cn.dev33.satok"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/security/SaTokenDaoRedisJackson.java",
    "chars": 9630,
    "preview": "//\n// Source code recreated from a .class file by IntelliJ IDEA\n// (powered by FernFlower decompiler)\n//\n\npackage im.zha"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/security/StpInterfaceImpl.java",
    "chars": 1140,
    "preview": "package im.zhaojun.zfile.core.config.security;\n\nimport cn.dev33.satoken.stp.StpInterface;\nimport cn.hutool.core.convert."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/spring/JacksonEnumDeserializer.java",
    "chars": 2122,
    "preview": "package im.zhaojun.zfile.core.config.spring;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/spring/SpringCacheConfig.java",
    "chars": 1297,
    "preview": "package im.zhaojun.zfile.core.config.spring;\n\nimport im.zhaojun.zfile.core.config.security.SaTokenDaoRedisJackson;\nimpor"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/spring/StringToEnumConverterFactory.java",
    "chars": 4048,
    "preview": "package im.zhaojun.zfile.core.config.spring;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport com.baomidou."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/spring/WebMvcConfig.java",
    "chars": 1362,
    "preview": "package im.zhaojun.zfile.core.config.spring;\n\nimport im.zhaojun.zfile.module.storage.model.enums.StorageTypeEnum;\nimport"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/totp/TotpAutoConfiguration.java",
    "chars": 2536,
    "preview": "package im.zhaojun.zfile.core.config.totp;\n\nimport dev.samstevens.totp.TotpInfo;\nimport dev.samstevens.totp.code.*;\nimpo"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/config/totp/TotpProperties.java",
    "chars": 1832,
    "preview": "package im.zhaojun.zfile.core.config.totp;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/constant/MdcConstant.java",
    "chars": 255,
    "preview": "package im.zhaojun.zfile.core.constant;\n\n/**\n * Slf4j mdc 常量\n *\n * @author zhaojun\n */\npublic class MdcConstant {\n\t\n\tpub"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/constant/RuleTypeConstant.java",
    "chars": 328,
    "preview": "package im.zhaojun.zfile.core.constant;\n\n/**\n * 规则表达式类型常量\n *\n * @author zhaojun\n */\npublic class RuleTypeConstant {\n\n   "
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/constant/ZFileConstant.java",
    "chars": 620,
    "preview": "package im.zhaojun.zfile.core.constant;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.sprin"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/constant/ZFileHttpHeaderConstant.java",
    "chars": 321,
    "preview": "package im.zhaojun.zfile.core.constant;\n\n/**\n * ZFile 自定义 HTTP 请求头常量\n *\n * @author zhaojun\n */\npublic class ZFileHttpHea"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/controller/FrontIndexController.java",
    "chars": 3298,
    "preview": "package im.zhaojun.zfile.core.controller;\n\nimport im.zhaojun.zfile.core.util.StringUtils;\nimport im.zhaojun.zfile.module"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/controller/LogController.java",
    "chars": 1463,
    "preview": "package im.zhaojun.zfile.core.controller;\n\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.util.ZipUtil;\nimpo"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/ErrorCode.java",
    "chars": 4939,
    "preview": "package im.zhaojun.zfile.core.exception;\n\nimport lombok.Getter;\n\n/**\n * 异常信息枚举类\n *\n * @author zhaojun\n */\n@Getter\npublic"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/GlobalExceptionHandler.java",
    "chars": 14607,
    "preview": "package im.zhaojun.zfile.core.exception;\n\nimport cn.dev33.satoken.exception.NotLoginException;\nimport cn.dev33.satoken.e"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/biz/APIHttpRequestBizException.java",
    "chars": 884,
    "preview": "package im.zhaojun.zfile.core.exception.biz;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfile."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/biz/CorsBizException.java",
    "chars": 408,
    "preview": "package im.zhaojun.zfile.core.exception.biz;\n\nimport im.zhaojun.zfile.core.exception.core.BizException;\nimport lombok.Ge"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/biz/FilePathSecurityBizException.java",
    "chars": 693,
    "preview": "package im.zhaojun.zfile.core.exception.biz;\n\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfile"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/biz/GetPreviewTextContentBizException.java",
    "chars": 666,
    "preview": "package im.zhaojun.zfile.core.exception.biz;\n\nimport im.zhaojun.zfile.core.exception.core.BizException;\nimport im.zhaoju"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/biz/InitializeStorageSourceBizException.java",
    "chars": 842,
    "preview": "package im.zhaojun.zfile.core.exception.biz;\n\nimport im.zhaojun.zfile.core.exception.GlobalExceptionHandler;\nimport im.z"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/biz/InvalidStorageSourceBizException.java",
    "chars": 710,
    "preview": "package im.zhaojun.zfile.core.exception.biz;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfile."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/biz/StorageSourceFileForbiddenAccessBizException.java",
    "chars": 833,
    "preview": "package im.zhaojun.zfile.core.exception.biz;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfile."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/biz/StorageSourceIllegalOperationBizException.java",
    "chars": 931,
    "preview": "package im.zhaojun.zfile.core.exception.biz;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfile."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/core/BizException.java",
    "chars": 2165,
    "preview": "package im.zhaojun.zfile.core.exception.core;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport lombok.Getter;\n\n"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/core/ErrorPageBizException.java",
    "chars": 2399,
    "preview": "package im.zhaojun.zfile.core.exception.core;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport lombok.Getter;\n\n"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/core/SystemException.java",
    "chars": 2028,
    "preview": "package im.zhaojun.zfile.core.exception.core;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport lombok.Getter;\n\n"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/status/BadRequestAccessException.java",
    "chars": 511,
    "preview": "package im.zhaojun.zfile.core.exception.status;\n\nimport im.zhaojun.zfile.core.exception.GlobalExceptionHandler;\nimport i"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/status/ForbiddenAccessException.java",
    "chars": 552,
    "preview": "package im.zhaojun.zfile.core.exception.status;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfi"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/status/MethodNotAllowedAccessException.java",
    "chars": 592,
    "preview": "package im.zhaojun.zfile.core.exception.status;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfi"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/status/NotFoundAccessException.java",
    "chars": 507,
    "preview": "package im.zhaojun.zfile.core.exception.status;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfi"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/status/UnauthorizedAccessException.java",
    "chars": 455,
    "preview": "package im.zhaojun.zfile.core.exception.status;\n\nimport im.zhaojun.zfile.core.exception.core.BizException;\n\n/**\n * 禁止访问异"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/system/UploadFileFailSystemException.java",
    "chars": 1557,
    "preview": "package im.zhaojun.zfile.core.exception.system;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfi"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/exception/system/ZFileAuthorizationSystemException.java",
    "chars": 817,
    "preview": "package im.zhaojun.zfile.core.exception.system;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zfi"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/filter/CorsFilter.java",
    "chars": 2194,
    "preview": "package im.zhaojun.zfile.core.filter;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport im.zhaojun.zfile.core.constant.ZFil"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/filter/MDCFilter.java",
    "chars": 1216,
    "preview": "package im.zhaojun.zfile.core.filter;\n\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.extra.servlet.JakartaServletU"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/filter/SecurityFilter.java",
    "chars": 3438,
    "preview": "package im.zhaojun.zfile.core.filter;\n\nimport cn.hutool.extra.servlet.JakartaServletUtil;\nimport cn.hutool.extra.spring."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/io/EnsureContentLengthInputStreamResource.java",
    "chars": 2377,
    "preview": "/*\n * Copyright 2002-2018 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/model/request/PageQueryRequest.java",
    "chars": 713,
    "preview": "package im.zhaojun.zfile.core.model.request;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport io.swagger"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/AjaxJson.java",
    "chars": 2537,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport io.swagger.v3.oas.annotati"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/ArrayUtils.java",
    "chars": 620,
    "preview": "package im.zhaojun.zfile.core.util;\n\n/**\n * 数组工具类\n *\n * @author zhaojun\n */\npublic class ArrayUtils {\n\n    /**\n     * 数组"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/CharPool.java",
    "chars": 150,
    "preview": "package im.zhaojun.zfile.core.util;\n\npublic interface CharPool {\n\n    /**\n     * CHAR 常量:斜杠 {@code '/'} ASCII 47\n     */"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/CharSequenceUtil.java",
    "chars": 18077,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.core.text.StrSplitter;\nimport jakarta.annotation.Nullable;\nimport "
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/ClassUtils.java",
    "chars": 995,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.ParameterizedType;\nimport "
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/CollectionUtils.java",
    "chars": 2897,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.core.lang.func.Func1;\n\nimport javax.annotation.Nullable;\nimport ja"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/DnsUtil.java",
    "chars": 1445,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport com.alibaba.dcm.DnsCacheManipulator;\nimport com.alibaba.fastjson2.JSONArray;"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/EnumConvertUtils.java",
    "chars": 1867,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.util.ClassUtil;\nimport"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/FileComparator.java",
    "chars": 1738,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.core.comparator.CompareUtil;\nimport im.zhaojun.zfile.module.storag"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/FileResponseUtil.java",
    "chars": 1768,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.core.io.FileUtil;\nimport im.zhaojun.zfile.core.exception.ErrorCode"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/FileSizeConverter.java",
    "chars": 2110,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class FileS"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/FileUtils.java",
    "chars": 1182,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport org.apache.commons.io.FilenameUtils;\n\n/**\n * 文件相关工具类\n *\n * @author zhaojun\n "
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/HttpUtil.java",
    "chars": 1573,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport im.zhaojun.zfile.core.constant.ZFileConstant;\nimport im.zhaojun.zfile.core.e"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/NaturalOrderComparator.java",
    "chars": 4522,
    "preview": "package im.zhaojun.zfile.core.util;\n/*\n NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/NumberUtils.java",
    "chars": 328,
    "preview": "package im.zhaojun.zfile.core.util;\n\n/**\n * 数字工具类\n *\n * @author zhaojun\n */\npublic class NumberUtils {\n\n    public stati"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/OnlyOfficeKeyCacheUtils.java",
    "chars": 4669,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.cache.Cache;\nimport cn.hutool.cache.CacheUtil;\nimport cn.hutool.ca"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/PatternMatcherUtils.java",
    "chars": 1676,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport java.nio.file.FileSystems;\nimport java.nio.file.PathMatcher;\nimport java.nio"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/PlaceholderUtils.java",
    "chars": 4361,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.extra.spring.SpringUtil;\nimport lombok.extern.slf4j.Slf4j;\n\nimport"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/ProxyDownloadUrlUtils.java",
    "chars": 3421,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.date.DateUtil;\nimport "
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/RequestHolder.java",
    "chars": 8642,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.extra.servlet.JakartaServletUtil;\nimport im.zhaojun.zfile.core.con"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/RequestUtils.java",
    "chars": 553,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.springframework.http.Htt"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/SizeToStrUtils.java",
    "chars": 1142,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.core.util.NumberUtil;\n\n/**\n * 文件大小或带宽大小转可读单位\n *\n * @author zhaojun"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/SpringMvcUtils.java",
    "chars": 751,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport org.springframework.util.AntPathMatcher;\nimport org.springframework.web.serv"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/StrPool.java",
    "chars": 1541,
    "preview": "package im.zhaojun.zfile.core.util;\n\npublic interface StrPool {\n\n    /**\n     * 字符串常量:制表符 {@code \"\\t\"}\n     */\n    Strin"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/StringUtils.java",
    "chars": 12017,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.cache.CacheUtil;\nimport cn.hutool.cache.impl.LRUCache;\nimport cn.h"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/UrlUtils.java",
    "chars": 1924,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.hutool.core.util.StrUtil;\n\n/**\n * url 相关工具类\n *\n * @author zhaojun\n */\npub"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/ZFileAuthUtil.java",
    "chars": 1341,
    "preview": "package im.zhaojun.zfile.core.util;\n\nimport cn.dev33.satoken.stp.StpUtil;\nimport cn.hutool.extra.spring.SpringUtil;\nimpo"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/matcher/AbstractRuleMatcher.java",
    "chars": 1165,
    "preview": "package im.zhaojun.zfile.core.util.matcher;\n\nimport java.util.Collection;\n\n/**\n * 抽象规则匹配器, 实现了部分方法, 用于简化规则匹配器的实现.\n *\n * "
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/matcher/IRuleMatcher.java",
    "chars": 1301,
    "preview": "package im.zhaojun.zfile.core.util.matcher;\n\nimport java.util.Collection;\n\n/**\n * 规则匹配器接口\n *\n * @author zhaojun\n */\npubl"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/matcher/RuleMatcherFactory.java",
    "chars": 1394,
    "preview": "package im.zhaojun.zfile.core.util.matcher;\n\nimport im.zhaojun.zfile.core.util.matcher.impl.AntPathRuleMatcher;\nimport i"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/matcher/impl/AntPathRuleMatcher.java",
    "chars": 861,
    "preview": "package im.zhaojun.zfile.core.util.matcher.impl;\n\nimport im.zhaojun.zfile.core.constant.RuleTypeConstant;\nimport im.zhao"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/matcher/impl/IpRuleMatcher.java",
    "chars": 8311,
    "preview": "package im.zhaojun.zfile.core.util.matcher.impl;\n\nimport im.zhaojun.zfile.core.constant.RuleTypeConstant;\nimport im.zhao"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/matcher/impl/RegexRuleMatcher.java",
    "chars": 1065,
    "preview": "package im.zhaojun.zfile.core.util.matcher.impl;\n\nimport cn.hutool.core.util.ReUtil;\nimport im.zhaojun.zfile.core.consta"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/util/matcher/impl/SpringSimpleRuleMatcher.java",
    "chars": 1013,
    "preview": "package im.zhaojun.zfile.core.util.matcher.impl;\n\nimport im.zhaojun.zfile.core.constant.RuleTypeConstant;\nimport im.zhao"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/validation/StringListValue.java",
    "chars": 761,
    "preview": "package im.zhaojun.zfile.core.validation;\n\nimport jakarta.validation.Constraint;\nimport jakarta.validation.Payload;\nimpo"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/core/validation/StringListValueConstraintValidator.java",
    "chars": 1020,
    "preview": "package im.zhaojun.zfile.core.validation;\n\nimport im.zhaojun.zfile.core.util.StringUtils;\nimport jakarta.validation.Cons"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/admin/controller/IpHelperController.java",
    "chars": 2106,
    "preview": "package im.zhaojun.zfile.module.admin.controller;\n\nimport cn.hutool.extra.servlet.JakartaServletUtil;\nimport com.alibaba"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/admin/controller/RuleMatcherTestController.java",
    "chars": 1844,
    "preview": "package im.zhaojun.zfile.module.admin.controller;\n\nimport im.zhaojun.zfile.core.util.AjaxJson;\nimport im.zhaojun.zfile.c"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/admin/model/request/TestRuleMatcherRequest.java",
    "chars": 363,
    "preview": "package im.zhaojun.zfile.module.admin.model.request;\n\nimport lombok.Data;\n\nimport jakarta.validation.constraints.NotBlan"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/annotation/JSONStringParse.java",
    "chars": 365,
    "preview": "package im.zhaojun.zfile.module.config.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/constant/SystemConfigConstant.java",
    "chars": 418,
    "preview": "package im.zhaojun.zfile.module.config.constant;\n\n/**\n * 系统设置字段常量.\n *\n * @author zhaojun\n */\npublic class SystemConfigCo"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/controller/SettingController.java",
    "chars": 4639,
    "preview": "package im.zhaojun.zfile.module.config.controller;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.alibaba.fastjson2.JSO"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/controller/SiteController.java",
    "chars": 3563,
    "preview": "package im.zhaojun.zfile.module.config.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\n"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/event/DirectLinkPrefixModifyHandler.java",
    "chars": 1588,
    "preview": "package im.zhaojun.zfile.module.config.event;\n\nimport im.zhaojun.zfile.core.util.StringUtils;\nimport im.zhaojun.zfile.mo"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/event/ISystemConfigModifyHandler.java",
    "chars": 479,
    "preview": "package im.zhaojun.zfile.module.config.event;\n\nimport im.zhaojun.zfile.module.config.model.entity.SystemConfig;\n\n/**\n * "
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/event/SecureLoginEntryModifyHandler.java",
    "chars": 1595,
    "preview": "package im.zhaojun.zfile.module.config.event;\n\nimport im.zhaojun.zfile.core.util.StringUtils;\nimport im.zhaojun.zfile.mo"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/event/SystemConfigModifyHandlerChain.java",
    "chars": 678,
    "preview": "package im.zhaojun.zfile.module.config.event;\n\nimport im.zhaojun.zfile.module.config.model.entity.SystemConfig;\nimport o"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/mapper/SystemConfigMapper.java",
    "chars": 857,
    "preview": "package im.zhaojun.zfile.module.config.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport im.zhaoju"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/dto/LinkExpireDTO.java",
    "chars": 299,
    "preview": "package im.zhaojun.zfile.module.config.model.dto;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\npublic class"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/dto/SystemConfigDTO.java",
    "chars": 9849,
    "preview": "package im.zhaojun.zfile.module.config.model.dto;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport im.zhaojun"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/entity/SystemConfig.java",
    "chars": 1205,
    "preview": "package im.zhaojun.zfile.module.config.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baom"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/enums/FileClickModeEnum.java",
    "chars": 482,
    "preview": "package im.zhaojun.zfile.module.config.model.enums;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport com.fa"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateAccessSettingRequest.java",
    "chars": 504,
    "preview": "package im.zhaojun.zfile.module.config.model.request;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateLinkSettingRequest.java",
    "chars": 1751,
    "preview": "package im.zhaojun.zfile.module.config.model.request;\n\nimport im.zhaojun.zfile.module.config.model.dto.LinkExpireDTO;\nim"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateSecuritySettingRequest.java",
    "chars": 1153,
    "preview": "package im.zhaojun.zfile.module.config.model.request;\n\nimport im.zhaojun.zfile.module.user.model.enums.LoginLogModeEnum;"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateSiteSettingRequest.java",
    "chars": 1802,
    "preview": "package im.zhaojun.zfile.module.config.model.request;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateUserNameAndPasswordRequest.java",
    "chars": 625,
    "preview": "package im.zhaojun.zfile.module.config.model.request;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/request/UpdateViewSettingRequest.java",
    "chars": 3336,
    "preview": "package im.zhaojun.zfile.module.config.model.request;\n\nimport im.zhaojun.zfile.module.config.model.enums.FileClickModeEn"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/model/result/FrontSiteConfigResult.java",
    "chars": 5385,
    "preview": "package im.zhaojun.zfile.module.config.model.result;\n\nimport im.zhaojun.zfile.module.config.model.dto.LinkExpireDTO;\nimp"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/config/service/SystemConfigService.java",
    "chars": 13030,
    "preview": "package im.zhaojun.zfile.module.config.service;\n\nimport cn.hutool.core.convert.Convert;\nimport cn.hutool.core.convert.Co"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/filter/controller/StorageSourceFilterController.java",
    "chars": 2016,
    "preview": "package im.zhaojun.zfile.module.filter.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\n"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/filter/mapper/FilterConfigMapper.java",
    "chars": 1271,
    "preview": "package im.zhaojun.zfile.module.filter.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport im.zhaoju"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/filter/model/entity/FilterConfig.java",
    "chars": 1568,
    "preview": "package im.zhaojun.zfile.module.filter.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baom"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/filter/model/enums/FilterConfigHiddenModeEnum.java",
    "chars": 551,
    "preview": "package im.zhaojun.zfile.module.filter.model.enums;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport com.fa"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/filter/service/FilterConfigService.java",
    "chars": 9620,
    "preview": "package im.zhaojun.zfile.module.filter.service;\n\nimport im.zhaojun.zfile.core.util.CollectionUtils;\nimport im.zhaojun.zf"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/install/controller/InstallController.java",
    "chars": 1408,
    "preview": "package im.zhaojun.zfile.module.install.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/install/model/request/InstallSystemRequest.java",
    "chars": 488,
    "preview": "package im.zhaojun.zfile.module.install.model.request;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/install/service/InstallService.java",
    "chars": 1764,
    "preview": "package im.zhaojun.zfile.module.install.service;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport im.zhaojun.zf"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/aspect/LinkRateLimiterAspect.java",
    "chars": 2236,
    "preview": "package im.zhaojun.zfile.module.link.aspect;\n\nimport cn.hutool.extra.servlet.JakartaServletUtil;\nimport im.zhaojun.zfile"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/aspect/RefererCheckAspect.java",
    "chars": 3781,
    "preview": "package im.zhaojun.zfile.module.link.aspect;\n\nimport im.zhaojun.zfile.core.util.CollectionUtils;\nimport im.zhaojun.zfile"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/cache/LinkRateLimiterCache.java",
    "chars": 1755,
    "preview": "package im.zhaojun.zfile.module.link.cache;\n\nimport cn.hutool.cache.impl.CacheObj;\nimport cn.hutool.cache.impl.TimedCach"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/controller/DirectLinkController.java",
    "chars": 4979,
    "preview": "package im.zhaojun.zfile.module.link.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\nim"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/controller/ShortLinkController.java",
    "chars": 3742,
    "preview": "package im.zhaojun.zfile.module.link.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\nim"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/controller/ShortLinkManagerController.java",
    "chars": 8029,
    "preview": "package im.zhaojun.zfile.module.link.controller;\n\nimport cn.hutool.core.io.IoUtil;\nimport cn.hutool.core.util.ObjUtil;\ni"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/convert/ShortLinkConvert.java",
    "chars": 827,
    "preview": "package im.zhaojun.zfile.module.link.convert;\n\nimport im.zhaojun.zfile.module.link.model.entity.ShortLink;\nimport im.zha"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/dto/DynamicRegisterMappingHandlerDTO.java",
    "chars": 387,
    "preview": "package im.zhaojun.zfile.module.link.dto;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.springframew"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/event/DeleteExpireLinkEvent.java",
    "chars": 111,
    "preview": "package im.zhaojun.zfile.module.link.event;\n\nimport lombok.Data;\n\n@Data\npublic class DeleteExpireLinkEvent {\n}\n"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/mapper/ShortLinkMapper.java",
    "chars": 1014,
    "preview": "package im.zhaojun.zfile.module.link.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport im.zhaojun."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/dto/CacheInfo.java",
    "chars": 230,
    "preview": "package im.zhaojun.zfile.module.link.model.dto;\n\nimport lombok.Data;\n\nimport java.util.Date;\n\n@Data\npublic class CacheIn"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/entity/ShortLink.java",
    "chars": 1609,
    "preview": "package im.zhaojun.zfile.module.link.model.entity;\n\nimport cn.hutool.core.date.DateUtil;\nimport com.baomidou.mybatisplus"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/enums/RefererTypeEnum.java",
    "chars": 520,
    "preview": "package im.zhaojun.zfile.module.link.model.enums;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport com.fast"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/request/BatchDeleteRequest.java",
    "chars": 198,
    "preview": "package im.zhaojun.zfile.module.link.model.request;\n\nimport lombok.Data;\n\nimport java.util.List;\n\n/**\n * @author zhaojun"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/request/BatchGenerateLinkRequest.java",
    "chars": 632,
    "preview": "package im.zhaojun.zfile.module.link.model.request;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Da"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/request/QueryDownloadLogRequest.java",
    "chars": 1363,
    "preview": "package im.zhaojun.zfile.module.link.model.request;\n\nimport cn.hutool.core.date.DateUtil;\nimport im.zhaojun.zfile.core.m"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/request/QueryLoginLogRequest.java",
    "chars": 1239,
    "preview": "package im.zhaojun.zfile.module.link.model.request;\n\nimport cn.hutool.core.date.DateUtil;\nimport im.zhaojun.zfile.core.m"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/request/QueryShortLinkLogRequest.java",
    "chars": 1019,
    "preview": "package im.zhaojun.zfile.module.link.model.request;\n\nimport cn.hutool.core.date.DateUtil;\nimport im.zhaojun.zfile.core.m"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/request/ShortLinkResult.java",
    "chars": 1053,
    "preview": "package im.zhaojun.zfile.module.link.model.request;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport im.zhaoj"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/request/ShortLinkSearchRequest.java",
    "chars": 1085,
    "preview": "package im.zhaojun.zfile.module.link.model.request;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Da"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/model/result/BatchGenerateLinkResponse.java",
    "chars": 316,
    "preview": "package im.zhaojun.zfile.module.link.model.result;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.All"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/service/DynamicDirectLinkPrefixService.java",
    "chars": 2058,
    "preview": "package im.zhaojun.zfile.module.link.service;\n\nimport im.zhaojun.zfile.module.link.dto.DynamicRegisterMappingHandlerDTO;"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/service/LinkDownloadService.java",
    "chars": 8290,
    "preview": "package im.zhaojun.zfile.module.link.service;\n\nimport cn.hutool.core.date.DateTime;\nimport cn.hutool.core.date.DateUtil;"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/link/service/ShortLinkService.java",
    "chars": 6565,
    "preview": "package im.zhaojun.zfile.module.link.service;\n\nimport cn.hutool.core.util.RandomUtil;\nimport com.baomidou.mybatisplus.co"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/controller/DownloadLogManagerController.java",
    "chars": 8056,
    "preview": "package im.zhaojun.zfile.module.log.controller;\n\nimport cn.hutool.core.util.ObjUtil;\nimport com.baomidou.mybatisplus.cor"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/controller/LoginLogController.java",
    "chars": 3291,
    "preview": "package im.zhaojun.zfile.module.log.controller;\n\nimport cn.hutool.core.util.ObjUtil;\nimport com.baomidou.mybatisplus.cor"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/convert/DownloadLogConvert.java",
    "chars": 756,
    "preview": "package im.zhaojun.zfile.module.log.convert;\n\nimport im.zhaojun.zfile.module.log.model.entity.DownloadLog;\nimport im.zha"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/mapper/DownloadLogMapper.java",
    "chars": 537,
    "preview": "package im.zhaojun.zfile.module.log.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport im.zhaojun.z"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/mapper/LoginLogMapper.java",
    "chars": 277,
    "preview": "package im.zhaojun.zfile.module.log.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport im.zhaojun.z"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/model/entity/DownloadLog.java",
    "chars": 2568,
    "preview": "package im.zhaojun.zfile.module.log.model.entity;\n\nimport cn.hutool.extra.servlet.JakartaServletUtil;\nimport com.baomido"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/model/entity/LoginLog.java",
    "chars": 865,
    "preview": "package im.zhaojun.zfile.module.log.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\n\nim"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/model/result/DownloadLogResult.java",
    "chars": 967,
    "preview": "package im.zhaojun.zfile.module.log.model.result;\n\nimport im.zhaojun.zfile.module.storage.model.enums.StorageTypeEnum;\ni"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/service/DownloadLogService.java",
    "chars": 2731,
    "preview": "package im.zhaojun.zfile.module.log.service;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomid"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/log/service/LoginLogService.java",
    "chars": 724,
    "preview": "package im.zhaojun.zfile.module.log.service;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomid"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/onlyoffice/controller/OnlyOfficeController.java",
    "chars": 10484,
    "preview": "package im.zhaojun.zfile.module.onlyoffice.controller;\n\nimport cn.dev33.satoken.stp.StpUtil;\nimport cn.hutool.core.colle"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/onlyoffice/model/OnlyOfficeCallback.java",
    "chars": 1910,
    "preview": "package im.zhaojun.zfile.module.onlyoffice.model;\n\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport lombok.Dat"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/onlyoffice/model/OnlyOfficeFile.java",
    "chars": 229,
    "preview": "package im.zhaojun.zfile.module.onlyoffice.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\n@Data\n@AllArgs"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/password/controller/StorageSourcePasswordController.java",
    "chars": 2060,
    "preview": "package im.zhaojun.zfile.module.password.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/password/mapper/PasswordConfigMapper.java",
    "chars": 835,
    "preview": "package im.zhaojun.zfile.module.password.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport im.zhao"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/password/model/dto/VerifyResultDTO.java",
    "chars": 1082,
    "preview": "package im.zhaojun.zfile.module.password.model.dto;\n\nimport im.zhaojun.zfile.core.exception.ErrorCode;\nimport lombok.Dat"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/password/model/entity/PasswordConfig.java",
    "chars": 1423,
    "preview": "package im.zhaojun.zfile.module.password.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.ba"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/password/service/PasswordConfigService.java",
    "chars": 9768,
    "preview": "package im.zhaojun.zfile.module.password.service;\n\nimport cn.hutool.core.util.ObjectUtil;\nimport im.zhaojun.zfile.core.e"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/permission/controller/PermissionController.java",
    "chars": 1476,
    "preview": "package im.zhaojun.zfile.module.permission.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiSort;\nimport i"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/permission/controller/StorageSourcePermissionController.java",
    "chars": 2299,
    "preview": "package im.zhaojun.zfile.module.permission.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSuppo"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/permission/convert/PermissionConfigConvert.java",
    "chars": 610,
    "preview": "package im.zhaojun.zfile.module.permission.convert;\n\nimport im.zhaojun.zfile.module.permission.model.entity.PermissionCo"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/permission/mapper/PermissionConfigMapper.java",
    "chars": 837,
    "preview": "package im.zhaojun.zfile.module.permission.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport im.zh"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/permission/model/entity/PermissionConfig.java",
    "chars": 1770,
    "preview": "package im.zhaojun.zfile.module.permission.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/permission/model/result/PermissionConfigResult.java",
    "chars": 486,
    "preview": "package im.zhaojun.zfile.module.permission.model.result;\n\nimport im.zhaojun.zfile.module.storage.model.enums.FileOperato"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/permission/model/result/PermissionInfoResult.java",
    "chars": 332,
    "preview": "package im.zhaojun.zfile.module.permission.model.result;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lomb"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/permission/service/PermissionConfigService.java",
    "chars": 3008,
    "preview": "package im.zhaojun.zfile.module.permission.service;\n\nimport im.zhaojun.zfile.module.permission.mapper.PermissionConfigMa"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/readme/controller/StorageSourceReadmeController.java",
    "chars": 2027,
    "preview": "package im.zhaojun.zfile.module.readme.controller;\n\nimport com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;\n"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/readme/mapper/ReadmeConfigMapper.java",
    "chars": 761,
    "preview": "package im.zhaojun.zfile.module.readme.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport im.zhaoju"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/readme/model/entity/ReadmeConfig.java",
    "chars": 1761,
    "preview": "package im.zhaojun.zfile.module.readme.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baom"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/readme/model/enums/ReadmeDisplayModeEnum.java",
    "chars": 493,
    "preview": "package im.zhaojun.zfile.module.readme.model.enums;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport com.fa"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/readme/model/enums/ReadmePathModeEnum.java",
    "chars": 471,
    "preview": "package im.zhaojun.zfile.module.readme.model.enums;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport com.fa"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/readme/service/ReadmeConfigService.java",
    "chars": 8350,
    "preview": "package im.zhaojun.zfile.module.readme.service;\n\nimport im.zhaojun.zfile.core.util.CollectionUtils;\nimport im.zhaojun.zf"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/share/context/ShareAccessContext.java",
    "chars": 2477,
    "preview": "package im.zhaojun.zfile.module.share.context;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport org.sprin"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/share/controller/ShareFileManagerController.java",
    "chars": 1840,
    "preview": "package im.zhaojun.zfile.module.share.controller;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nim"
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/share/controller/ShareLinkController.java",
    "chars": 6826,
    "preview": "package im.zhaojun.zfile.module.share.controller;\n\nimport cn.dev33.satoken.annotation.SaCheckLogin;\nimport com.baomidou."
  },
  {
    "path": "src/main/java/im/zhaojun/zfile/module/share/mapper/ShareLinkMapper.java",
    "chars": 1106,
    "preview": "package im.zhaojun.zfile.module.share.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport im.zhaojun"
  }
]

// ... and 365 more files (download for full content)

About this extraction

This page contains the full source code of the zfile-dev/zfile GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 565 files (1.1 MB), approximately 311.0k tokens, and a symbol index with 1674 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!