diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index a014a1b8c..12e77d40f 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -689,10 +689,10 @@ DROP TABLE IF EXISTS `infra_file`; CREATE TABLE `infra_file` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '文件编号', `config_id` bigint NULL DEFAULT NULL COMMENT '配置编号', - `name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件名', + `name` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件名', `path` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件路径', `url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件 URL', - `type` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件类型', + `type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件MIME类型', `size` int NOT NULL COMMENT '文件大小', `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', diff --git a/sql/oracle/ruoyi-vue-pro.sql b/sql/oracle/ruoyi-vue-pro.sql index 1f4908645..07e0a85e3 100644 --- a/sql/oracle/ruoyi-vue-pro.sql +++ b/sql/oracle/ruoyi-vue-pro.sql @@ -878,7 +878,7 @@ CREATE TABLE "INFRA_FILE" ( "CONFIG_ID" NUMBER(20,0), "PATH" NVARCHAR2(512), "URL" NCLOB, - "TYPE" NVARCHAR2(63), + "TYPE" NVARCHAR2(64), "SIZE" NUMBER(11,0) NOT NULL, "CREATOR" NVARCHAR2(64), "CREATE_TIME" DATE NOT NULL, @@ -908,7 +908,7 @@ COMMENT ON COLUMN "INFRA_FILE"."ID" IS '文件编号'; COMMENT ON COLUMN "INFRA_FILE"."CONFIG_ID" IS '配置编号'; COMMENT ON COLUMN "INFRA_FILE"."PATH" IS '文件路径'; COMMENT ON COLUMN "INFRA_FILE"."URL" IS '文件 URL'; -COMMENT ON COLUMN "INFRA_FILE"."TYPE" IS '文件类型'; +COMMENT ON COLUMN "INFRA_FILE"."TYPE" IS '文件MIME类型'; COMMENT ON COLUMN "INFRA_FILE"."SIZE" IS '文件大小'; COMMENT ON COLUMN "INFRA_FILE"."CREATOR" IS '创建者'; COMMENT ON COLUMN "INFRA_FILE"."CREATE_TIME" IS '创建时间'; diff --git a/sql/postgresql/ruoyi-vue-pro.sql b/sql/postgresql/ruoyi-vue-pro.sql index 9e80ce70d..a6575bccb 100644 --- a/sql/postgresql/ruoyi-vue-pro.sql +++ b/sql/postgresql/ruoyi-vue-pro.sql @@ -1717,21 +1717,21 @@ CREATE TABLE "infra_file" ( "config_id" int8, "path" varchar(512) COLLATE "pg_catalog"."default" NOT NULL, "url" varchar(1024) COLLATE "pg_catalog"."default" NOT NULL, - "type" varchar(63) COLLATE "pg_catalog"."default", + "type" varchar(64) COLLATE "pg_catalog"."default", "size" int4 NOT NULL, "creator" varchar(64) COLLATE "pg_catalog"."default", "create_time" timestamp(6) NOT NULL, "updater" varchar(64) COLLATE "pg_catalog"."default", "update_time" timestamp(6) NOT NULL, "deleted" int2 NOT NULL DEFAULT 0, - "name" varchar(255) COLLATE "pg_catalog"."default" + "name" varchar(512) COLLATE "pg_catalog"."default" ) ; COMMENT ON COLUMN "infra_file"."id" IS '文件编号'; COMMENT ON COLUMN "infra_file"."config_id" IS '配置编号'; COMMENT ON COLUMN "infra_file"."path" IS '文件路径'; COMMENT ON COLUMN "infra_file"."url" IS '文件 URL'; -COMMENT ON COLUMN "infra_file"."type" IS '文件类型'; +COMMENT ON COLUMN "infra_file"."type" IS '文件MIME类型'; COMMENT ON COLUMN "infra_file"."size" IS '文件大小'; COMMENT ON COLUMN "infra_file"."creator" IS '创建者'; COMMENT ON COLUMN "infra_file"."create_time" IS '创建时间'; diff --git a/sql/sqlserver/ruoyi-vue-pro.sql b/sql/sqlserver/ruoyi-vue-pro.sql index d6bd1847c..1c0d93776 100644 --- a/sql/sqlserver/ruoyi-vue-pro.sql +++ b/sql/sqlserver/ruoyi-vue-pro.sql @@ -2634,14 +2634,14 @@ CREATE TABLE [dbo].[infra_file] ( [config_id] bigint NULL, [path] nvarchar(512) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, [url] nvarchar(1024) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, - [type] nvarchar(63) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [type] nvarchar(64) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [size] int NOT NULL, [creator] nvarchar(64) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [create_time] datetime2(7) NOT NULL, [updater] nvarchar(64) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, [update_time] datetime2(7) NOT NULL, [deleted] bit DEFAULT 0 NOT NULL, - [name] nvarchar(256) COLLATE SQL_Latin1_General_CP1_CI_AS NULL + [name] nvarchar(512) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ) GO @@ -2677,7 +2677,7 @@ EXEC sp_addextendedproperty GO EXEC sp_addextendedproperty -'MS_Description', N'文件类型', +'MS_Description', N'文件 MIME 类型', 'SCHEMA', N'dbo', 'TABLE', N'infra_file', 'COLUMN', N'type' diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileUtils.java index 89fab2da2..f6cb2c82d 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileUtils.java @@ -1,9 +1,14 @@ package cn.iocoder.yudao.framework.common.util.io; +import cn.hutool.core.io.FileTypeUtil; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.file.FileNameUtil; import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.digest.DigestUtil; import lombok.SneakyThrows; +import java.io.ByteArrayInputStream; import java.io.File; /** @@ -58,8 +63,20 @@ public class FileUtils { return file; } - - public static void main(String[] args) { + /** + * @param content 文件内容 + * @param originalName 原始文件名 + * @return path,唯一不可重复 + */ + public static String generatePath(byte[] content, String originalName) { + String sha256Hex = DigestUtil.sha256Hex(content); + // 如果存在name,则优先使用name的后缀 + if (StrUtil.isNotBlank(originalName)) { + String extName = FileNameUtil.extName(originalName); + return StrUtil.isBlank(extName) ? sha256Hex : sha256Hex + "." + extName; + } else { + return sha256Hex + '.' + FileTypeUtil.getType(new ByteArrayInputStream(content)); + } } } diff --git a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/utils/FileTypeUtils.java b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/utils/FileTypeUtils.java index d21b4879a..8b99227b1 100644 --- a/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/utils/FileTypeUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/utils/FileTypeUtils.java @@ -4,8 +4,6 @@ import com.alibaba.ttl.TransmittableThreadLocal; import lombok.SneakyThrows; import org.apache.tika.Tika; -import java.io.ByteArrayInputStream; - /** * 文件类型 Utils * @@ -16,14 +14,35 @@ public class FileTypeUtils { private static final ThreadLocal TIKA = TransmittableThreadLocal.withInitial(Tika::new); /** - * 获得文件的 mineType + * 获得文件的 mineType,对于doc,jar等文件会有误差 * * @param data 文件内容 - * @return mineType + * @return mineType 无法识别时会返回“application/octet-stream” */ @SneakyThrows public static String getMineType(byte[] data) { - return TIKA.get().detect(new ByteArrayInputStream(data)); + return TIKA.get().detect(data); + } + + /** + * 已知文件名,获取文件类型,在某些情况下比通过字节数组准确,例如使用jar文件时,通过名字更为准确 + * + * @param name 文件名 + * @return mineType 无法识别时会返回“application/octet-stream” + */ + public static String getMineType(String name) { + return TIKA.get().detect(name); + } + + /** + * 在拥有文件和数据的情况下,最好使用此方法,最为准确 + * + * @param data 文件内容 + * @param name 文件名 + * @return mineType 无法识别时会返回“application/octet-stream” + */ + public static String getMineType(byte[] data, String name) { + return TIKA.get().detect(data, name); } } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java index 346314e83..e803fcf25 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java @@ -21,7 +21,7 @@ public class FilePageReqVO extends PageParam { @ApiModelProperty(value = "文件路径", example = "yudao", notes = "模糊匹配") private String path; - @ApiModelProperty(value = "文件类型", example = "jpg", notes = "模糊匹配") + @ApiModelProperty(value = "文件类型", example = "application/octet-stream", notes = "模糊匹配") private String type; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java index 10737693d..31b790a21 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java @@ -13,6 +13,9 @@ public class FileRespVO { @ApiModelProperty(value = "文件编号", required = true, example = "1024") private Long id; + @ApiModelProperty(value = "配置编号", required = true, example = "11") + private Long configId; + @ApiModelProperty(value = "文件路径", required = true, example = "yudao.jpg") private String path; @@ -22,7 +25,7 @@ public class FileRespVO { @ApiModelProperty(value = "文件 URL", required = true, example = "https://www.iocoder.cn/yudao.jpg") private String url; - @ApiModelProperty(value = "文件类型", example = "jpg") + @ApiModelProperty(value = "文件MIME类型", example = "application/octet-stream") private String type; @ApiModelProperty(value = "文件大小", example = "2048", required = true) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java index 7e81280da..7c242d4eb 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java @@ -46,9 +46,7 @@ public class FileDO extends BaseDO { */ private String url; /** - * 文件类型 - * - * 通过 {@link cn.hutool.core.io.FileTypeUtil#getType(InputStream)} 获取 + * 文件的MIME类型,例如"application/octet-stream" */ private String type; /** diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java index 98af005f7..c69eddfa4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java @@ -1,9 +1,10 @@ package cn.iocoder.yudao.module.infra.service.file; +import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.digest.DigestUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.io.FileUtils; import cn.iocoder.yudao.framework.file.core.client.FileClient; import cn.iocoder.yudao.framework.file.core.utils.FileTypeUtils; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; @@ -40,10 +41,9 @@ public class FileServiceImpl implements FileService { @SneakyThrows public String createFile(String name, String path, byte[] content) { // 计算默认的 path 名 - String type = FileTypeUtils.getMineType(content); + String type = FileTypeUtils.getMineType(content, name); if (StrUtil.isEmpty(path)) { - path = DigestUtil.md5Hex(content) - + '.' + StrUtil.subAfter(type, '/', true); // 文件的后缀 + path = FileUtils.generatePath(content, name); } // 如果 name 为空,则使用 path 填充 if (StrUtil.isEmpty(name)) { diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java index e61039385..b5acbb8a4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceTest.java @@ -40,7 +40,7 @@ public class FileServiceTest extends BaseDbUnitTest { // mock 数据 FileDO dbFile = randomPojo(FileDO.class, o -> { // 等会查询到 o.setPath("yunai"); - o.setType("jpg"); + o.setType("image/jpg"); o.setCreateTime(buildTime(2021, 1, 15)); }); fileMapper.insert(dbFile); @@ -48,7 +48,7 @@ public class FileServiceTest extends BaseDbUnitTest { fileMapper.insert(ObjectUtils.cloneIgnoreId(dbFile, o -> o.setPath("tudou"))); // 测试 type 不匹配 fileMapper.insert(ObjectUtils.cloneIgnoreId(dbFile, o -> { - o.setType("png"); + o.setType("image/png"); })); // 测试 createTime 不匹配 fileMapper.insert(ObjectUtils.cloneIgnoreId(dbFile, o -> { @@ -90,7 +90,7 @@ public class FileServiceTest extends BaseDbUnitTest { assertEquals(10L, file.getConfigId()); assertEquals(path, file.getPath()); assertEquals(url, file.getUrl()); - assertEquals("jpg", file.getType()); + assertEquals("image/jpg", file.getType()); assertEquals(content.length, file.getSize()); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java index c5dd5f4ac..0f4e41f96 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.social; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; @@ -21,6 +22,11 @@ import lombok.*; @AllArgsConstructor public class SocialUserBindDO extends BaseDO { + /** + * 编号 + */ + @TableId + private Long id; /** * 关联的用户编号 * diff --git a/yudao-ui-admin/src/views/infra/file/index.vue b/yudao-ui-admin/src/views/infra/file/index.vue index 462a879de..53716782e 100644 --- a/yudao-ui-admin/src/views/infra/file/index.vue +++ b/yudao-ui-admin/src/views/infra/file/index.vue @@ -26,27 +26,32 @@ - - - - - - - - - - - - - + + + + + + + + + - + @@ -77,14 +82,18 @@