mirror of
https://gitee.com/xiangheng/x_admin.git
synced 2025-10-06 16:47:06 +08:00
统一上传文件不区分图片、视频接口,素材中心私有化
This commit is contained in:
@@ -8,7 +8,7 @@ import { getToken } from '@/utils/auth'
|
||||
type album_cate = {
|
||||
id?: number
|
||||
pid?: number
|
||||
type?: number
|
||||
// type?: number
|
||||
name?: string
|
||||
isDelete?: number
|
||||
createTime?: string
|
||||
@@ -18,7 +18,7 @@ type album_cate = {
|
||||
// 查询
|
||||
type album_cate_query = {
|
||||
pid?: number
|
||||
type?: number
|
||||
// type?: number
|
||||
name?: string
|
||||
createTimeStart?: string
|
||||
createTimeEnd?: string
|
||||
@@ -29,7 +29,7 @@ type album_cate_query = {
|
||||
type album_cate_edit = {
|
||||
id?: number
|
||||
pid?: number
|
||||
type?: number
|
||||
// type?: number
|
||||
name?: string
|
||||
}
|
||||
|
||||
|
@@ -28,12 +28,25 @@ export default defineComponent({
|
||||
default: '100px'
|
||||
},
|
||||
// 文件类型
|
||||
type: {
|
||||
ext: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
emits: ['close']
|
||||
emits: ['close'],
|
||||
computed: {
|
||||
type() {
|
||||
const imageExt = ['jpg', 'jpeg', 'png', 'gif', 'bmp']
|
||||
const videoExt = ['mp4', 'avi', 'mov']
|
||||
if (imageExt.includes(this.ext)) {
|
||||
return 'image'
|
||||
}
|
||||
if (videoExt.includes(this.ext)) {
|
||||
return 'video'
|
||||
}
|
||||
return 'file'
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@@ -15,7 +15,7 @@ import { shallowRef, ref, reactive } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
// 左侧分组的钩子函数
|
||||
export function useCate(type: number) {
|
||||
export function useCate() {
|
||||
const treeRef = shallowRef<InstanceType<typeof ElTree>>()
|
||||
// 分组列表
|
||||
const cateLists = ref<any[]>([])
|
||||
@@ -25,18 +25,12 @@ export function useCate(type: number) {
|
||||
|
||||
// 获取分组列表
|
||||
const getCateLists = async () => {
|
||||
const data = await fileCateLists({
|
||||
type
|
||||
})
|
||||
const data = await fileCateLists({})
|
||||
const item: any[] = [
|
||||
{
|
||||
name: '全部',
|
||||
id: 0
|
||||
}
|
||||
// {
|
||||
// name: '未分组',
|
||||
// id: 0
|
||||
// }
|
||||
]
|
||||
cateLists.value = data
|
||||
cateLists.value.unshift(...item)
|
||||
@@ -48,7 +42,6 @@ export function useCate(type: number) {
|
||||
// 添加分组
|
||||
const handleAddCate = async (value: string) => {
|
||||
await fileCateAdd({
|
||||
type,
|
||||
name: value,
|
||||
pid: 0
|
||||
})
|
||||
@@ -92,7 +85,7 @@ export function useCate(type: number) {
|
||||
// 处理文件的钩子函数
|
||||
export function useFile(
|
||||
cateId: Ref<string | number>,
|
||||
type: Ref<number>,
|
||||
ext: string[],
|
||||
limit: Ref<number>,
|
||||
size: number
|
||||
) {
|
||||
@@ -103,7 +96,7 @@ export function useFile(
|
||||
const isIndeterminate = ref(false)
|
||||
const fileParams = reactive({
|
||||
name: '',
|
||||
type: type,
|
||||
ext: ext,
|
||||
cid: cateId
|
||||
})
|
||||
const { pager, getLists, resetPage } = usePaging({
|
||||
|
@@ -85,22 +85,10 @@
|
||||
<div class="operate-btn flex">
|
||||
<div class="flex-1 flex">
|
||||
<upload
|
||||
v-if="type == 'image'"
|
||||
v-perms="['admin:common:upload:image']"
|
||||
class="mr-3"
|
||||
:data="{ cid: cateId }"
|
||||
:type="type"
|
||||
:show-progress="true"
|
||||
@change="refresh"
|
||||
>
|
||||
<el-button type="primary">本地上传</el-button>
|
||||
</upload>
|
||||
<upload
|
||||
v-if="type == 'video'"
|
||||
v-perms="['admin:common:upload:video']"
|
||||
class="mr-3"
|
||||
:data="{ cid: cateId }"
|
||||
:type="type"
|
||||
:ext="ext"
|
||||
:show-progress="true"
|
||||
@change="refresh"
|
||||
>
|
||||
@@ -139,7 +127,7 @@
|
||||
<file-item
|
||||
:uri="row.uri"
|
||||
file-size="50px"
|
||||
:type="type"
|
||||
:ext="row.ext"
|
||||
@click.stop="handlePreview(row.uri)"
|
||||
></file-item>
|
||||
</template>
|
||||
@@ -151,6 +139,9 @@
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="大小" prop="size" min-width="100"> </el-table-column>
|
||||
<el-table-column label="格式" prop="ext" min-width="100"></el-table-column>
|
||||
|
||||
<el-table-column prop="createTime" label="上传时间" min-width="100" />
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
@@ -259,7 +250,7 @@
|
||||
<file-item
|
||||
:uri="item.uri"
|
||||
file-size="100px"
|
||||
:type="type"
|
||||
:ext="item.ext"
|
||||
></file-item>
|
||||
</del-wrap>
|
||||
</div>
|
||||
@@ -268,12 +259,14 @@
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
<preview v-model="showPreview" :url="previewUrl" :type="type" />
|
||||
<preview v-model="showPreview" :url="previewUrl" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, toRefs, ref, watch } from 'vue'
|
||||
import { onMounted, toRefs, ref, watch } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
import { useCate, useFile } from './hook'
|
||||
import FileItem from './file.vue'
|
||||
import Preview from './preview.vue'
|
||||
@@ -286,10 +279,14 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
ext: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
// type: {
|
||||
// type: String,
|
||||
// default: 'image'
|
||||
// },
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'picker'
|
||||
@@ -301,18 +298,18 @@ const props = defineProps({
|
||||
})
|
||||
const emit = defineEmits(['change'])
|
||||
const { limit } = toRefs(props)
|
||||
const typeValue = computed<number>(() => {
|
||||
switch (props.type) {
|
||||
case 'image':
|
||||
return 10
|
||||
case 'video':
|
||||
return 20
|
||||
case 'file':
|
||||
return 30
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
})
|
||||
// const typeValue = computed<number>(() => {
|
||||
// switch (props.type) {
|
||||
// case 'image':
|
||||
// return 10
|
||||
// case 'video':
|
||||
// return 20
|
||||
// case 'file':
|
||||
// return 30
|
||||
// default:
|
||||
// return 0
|
||||
// }
|
||||
// })
|
||||
// const visible: Ref<boolean> = inject('visible')
|
||||
const previewUrl = ref('')
|
||||
const showPreview = ref(false)
|
||||
@@ -325,7 +322,7 @@ const {
|
||||
handleDeleteCate,
|
||||
getCateLists,
|
||||
handleCatSelect
|
||||
} = useCate(typeValue.value)
|
||||
} = useCate()
|
||||
|
||||
const {
|
||||
tableRef,
|
||||
@@ -345,7 +342,7 @@ const {
|
||||
|
||||
selectItems,
|
||||
handleFileRename
|
||||
} = useFile(cateId, typeValue, limit, props.pageSize)
|
||||
} = useFile(cateId, props.ext, limit, props.pageSize)
|
||||
function handleSelectionChange(val: any[]) {
|
||||
console.log('handleSelectionChange', val)
|
||||
selectItems(val)
|
||||
@@ -356,22 +353,12 @@ const getData = async () => {
|
||||
treeRef.value?.setCurrentKey(cateId.value)
|
||||
getFileList()
|
||||
}
|
||||
// getData()
|
||||
|
||||
const handlePreview = (url: string) => {
|
||||
previewUrl.value = url
|
||||
showPreview.value = true
|
||||
}
|
||||
// watch(
|
||||
// () => visible,
|
||||
// (val) => {
|
||||
// if (val) {
|
||||
// getData()
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// immediate: true
|
||||
// }
|
||||
// )
|
||||
|
||||
watch(cateId, () => {
|
||||
fileParams.name = ''
|
||||
refresh()
|
||||
|
@@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, useTemplateRef, watch, nextTick } from 'vue'
|
||||
import { ref, useTemplateRef, watch, computed, nextTick } from 'vue'
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
@@ -26,11 +26,24 @@ const props = defineProps({
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
}
|
||||
// type: {
|
||||
// type: String,
|
||||
// default: 'image'
|
||||
// }
|
||||
})
|
||||
|
||||
const type = computed(() => {
|
||||
const imageExt = ['jpg', 'jpeg', 'png', 'gif', 'bmp']
|
||||
const videoExt = ['mp4', 'avi', 'mov']
|
||||
const ext = props.url.split('.').pop()
|
||||
if (imageExt.includes(ext)) {
|
||||
return 'image'
|
||||
}
|
||||
if (videoExt.includes(ext)) {
|
||||
return 'video'
|
||||
}
|
||||
return 'file'
|
||||
})
|
||||
|
||||
const playerRef = useTemplateRef('playerRef')
|
||||
|
@@ -14,7 +14,7 @@
|
||||
:on-error="handleError"
|
||||
:accept="getAccept"
|
||||
>
|
||||
<slot></slot>
|
||||
<slot></slot>{{ getAccept }}
|
||||
</el-upload>
|
||||
<el-dialog
|
||||
v-if="showProgress && fileList.length"
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref, toRaw, useTemplateRef } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import config from '@/config'
|
||||
import feedback from '@/utils/feedback'
|
||||
@@ -54,6 +55,10 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
ext: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
// 上传文件类型
|
||||
type: {
|
||||
type: String,
|
||||
@@ -94,7 +99,7 @@ export default defineComponent({
|
||||
action = `${config.baseUrl}${config.urlPrefix}${props.url}`
|
||||
}
|
||||
} else {
|
||||
action = `${config.baseUrl}${config.urlPrefix}/common/upload/${props.type}`
|
||||
action = `${config.baseUrl}${config.urlPrefix}/common/upload/file`
|
||||
}
|
||||
const headers = computed(() => ({
|
||||
token: userStore.token,
|
||||
@@ -135,14 +140,15 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const getAccept = computed(() => {
|
||||
switch (props.type) {
|
||||
case 'image':
|
||||
return '.jpg,.png,.gif,.webp,.jpeg,.ico,.bmp'
|
||||
case 'video':
|
||||
return '.wmv,.avi,.mov,.mp4,.flv,.rmvb'
|
||||
default:
|
||||
return '*'
|
||||
if (props.ext.length) {
|
||||
// 补充前缀
|
||||
return props.ext
|
||||
.map((item) => {
|
||||
return `.${item}`
|
||||
})
|
||||
.join(',')
|
||||
}
|
||||
return '*'
|
||||
})
|
||||
return {
|
||||
uploadRefs,
|
||||
|
@@ -5,13 +5,13 @@
|
||||
<el-tab-pane
|
||||
v-for="item in tabsMap"
|
||||
:label="item.name"
|
||||
:name="item.type"
|
||||
:index="item.type"
|
||||
:key="item.type"
|
||||
:name="item.name"
|
||||
:index="item.name"
|
||||
:key="item.name"
|
||||
lazy
|
||||
>
|
||||
<material
|
||||
:type="item.type"
|
||||
:ext="item.ext"
|
||||
mode="page"
|
||||
file-size="120px"
|
||||
:limit="-1"
|
||||
@@ -31,12 +31,24 @@ defineOptions({
|
||||
})
|
||||
const tabsMap = [
|
||||
{
|
||||
type: 'image',
|
||||
name: '图片'
|
||||
// type: '',
|
||||
name: '全部',
|
||||
ext: []
|
||||
},
|
||||
{
|
||||
type: 'video',
|
||||
name: '视频'
|
||||
// type: 'image',
|
||||
name: '图片',
|
||||
ext: ['jpg', 'jpeg', 'png', 'gif', 'bmp']
|
||||
},
|
||||
{
|
||||
// type: 'video',
|
||||
name: '视频',
|
||||
ext: ['mp4', 'avi', 'mov']
|
||||
},
|
||||
{
|
||||
// type: 'pdf',
|
||||
name: 'pdf',
|
||||
ext: ['pdf']
|
||||
}
|
||||
]
|
||||
const activeTab = ref('image')
|
||||
@@ -47,11 +59,7 @@ const activeTab = ref('image')
|
||||
min-width: 700px;
|
||||
:deep(.el-tabs) {
|
||||
height: calc(100vh - 180px);
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// .el-tabs__header {
|
||||
// margin-bottom: 0 !important;
|
||||
// }
|
||||
|
||||
.el-tabs__content,
|
||||
.el-tab-pane {
|
||||
min-height: 0;
|
||||
|
@@ -13,3 +13,11 @@ REDIS.URL='redis://localhost:6379'
|
||||
|
||||
# 上传文件目录
|
||||
FILE.UPLOAD_DIRECTORY='/www/wwwroot/x_admin_go/public/uploads/'
|
||||
|
||||
|
||||
GeTui.HOST=''
|
||||
GeTui.APPID=''
|
||||
GeTui.APPKEY=''
|
||||
GeTui.APPSECRET=''
|
||||
GeTui.MASTERSECRET=''
|
||||
GeTui.PackName=''
|
@@ -3,21 +3,30 @@ package config
|
||||
type fileConfig struct {
|
||||
UploadDirectory string `mapstructure:"UPLOAD_DIRECTORY"` // 文件目录
|
||||
PublicPrefix string `mapstructure:"PUBLIC_PREFIX"` // 资源访问前缀
|
||||
|
||||
UploadImageSize int64 `mapstructure:"UPLOAD_IMAGE_SIZE"` // 上传图片大小限制
|
||||
UploadVideoSize int64 `mapstructure:"UPLOAD_VIDEO_SIZE"` // 上传视频大小限制
|
||||
UploadFileSize int64 `mapstructure:"UPLOAD_FILE_SIZE"` // 上传文件大小限制
|
||||
|
||||
UploadImageExt []string `mapstructure:"UPLOAD_IMAGE_EXT"` // 上传图片扩展
|
||||
UploadVideoExt []string `mapstructure:"UPLOAD_VIDEO_EXT"` // 上传视频扩展
|
||||
UploadFileExt []string `mapstructure:"UPLOAD_FILE_EXT"` // 上传文件扩展
|
||||
}
|
||||
|
||||
// var uploadImageExtDefault = []string{"png", "jpg", "jpeg", "gif", "ico", "bmp", "webp", "avif"}
|
||||
|
||||
var FileConfig = fileConfig{
|
||||
// 资源访问前缀
|
||||
PublicPrefix: "/api/uploads",
|
||||
// 上传文件路径
|
||||
UploadDirectory: "/tmp/uploads/x_admin_go/",
|
||||
UploadImageSize: 10 * 1024 * 1024,
|
||||
UploadVideoSize: 30 * 1024 * 1024,
|
||||
UploadImageSize: 10 * 1024 * 1024, // 10MB
|
||||
UploadVideoSize: 500 * 1024 * 1024, // 500MB
|
||||
UploadFileSize: 1024 * 1024 * 1024, //1GB
|
||||
// 上传图片扩展
|
||||
UploadImageExt: []string{"png", "jpg", "jpeg", "gif", "ico", "bmp", "webp", "avif"},
|
||||
// 上传视频扩展
|
||||
UploadVideoExt: []string{"mp4", "mp3", "avi", "flv", "rmvb", "mov"},
|
||||
|
||||
UploadFileExt: []string{"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "zip", "rar", "7z", "txt"},
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package commonController
|
||||
|
||||
import (
|
||||
"x_admin/config"
|
||||
"x_admin/core/request"
|
||||
"x_admin/core/response"
|
||||
"x_admin/middleware"
|
||||
@@ -39,7 +40,8 @@ func (ah albumHandler) albumList(c *gin.Context) {
|
||||
if response.IsFailWithResp(c, util.VerifyUtil.VerifyQuery(c, &listReq)) {
|
||||
return
|
||||
}
|
||||
res, err := commonService.AlbumService.AlbumList(page, listReq)
|
||||
var adminId = config.AdminConfig.GetAdminId(c)
|
||||
res, err := commonService.AlbumService.AlbumList(adminId, page, listReq)
|
||||
response.CheckAndRespWithData(c, res, err)
|
||||
}
|
||||
|
||||
@@ -76,7 +78,8 @@ func (ah albumHandler) cateList(c *gin.Context) {
|
||||
if response.IsFailWithResp(c, util.VerifyUtil.VerifyQuery(c, &listReq)) {
|
||||
return
|
||||
}
|
||||
res, err := commonService.AlbumService.CateList(listReq)
|
||||
var adminId = config.AdminConfig.GetAdminId(c)
|
||||
res, err := commonService.AlbumService.CateList(adminId, listReq)
|
||||
response.CheckAndRespWithData(c, res, err)
|
||||
}
|
||||
|
||||
@@ -86,7 +89,8 @@ func (ah albumHandler) cateAdd(c *gin.Context) {
|
||||
if response.IsFailWithResp(c, util.VerifyUtil.VerifyJSON(c, &addReq)) {
|
||||
return
|
||||
}
|
||||
response.CheckAndResp(c, commonService.AlbumService.CateAdd(addReq))
|
||||
var adminId = config.AdminConfig.GetAdminId(c)
|
||||
response.CheckAndResp(c, commonService.AlbumService.CateAdd(adminId, addReq))
|
||||
}
|
||||
|
||||
// cateRename 类目命名
|
||||
|
@@ -15,28 +15,26 @@ func UploadRoute(rg *gin.RouterGroup) {
|
||||
handle := uploadHandler{}
|
||||
|
||||
rg = rg.Group("/common", middleware.TokenAuth())
|
||||
rg.POST("/upload/image", middleware.RecordLog("上传图片", middleware.RequestFile), handle.uploadImage)
|
||||
rg.POST("/upload/video", middleware.RecordLog("上传视频", middleware.RequestFile), handle.uploadVideo)
|
||||
rg.POST("/upload/preUploadFile", middleware.RecordLog("文件预上传", middleware.RequestFile), handle.preUploadFile)
|
||||
rg.POST("/upload/file", middleware.RecordLog("上传文件", middleware.RequestFile), handle.uploadFile)
|
||||
}
|
||||
|
||||
type uploadHandler struct{}
|
||||
|
||||
// uploadImage 上传图片
|
||||
func (uh uploadHandler) uploadImage(c *gin.Context) {
|
||||
var uReq commonSchema.CommonUploadImageReq
|
||||
if response.IsFailWithResp(c, util.VerifyUtil.VerifyBody(c, &uReq)) {
|
||||
return
|
||||
}
|
||||
file, ve := util.VerifyUtil.VerifyFile(c, "file")
|
||||
if response.IsFailWithResp(c, ve) {
|
||||
return
|
||||
}
|
||||
res, err := commonService.UploadService.UploadImage(file, uReq.Cid, config.AdminConfig.GetAdminId(c))
|
||||
response.CheckAndRespWithData(c, res, err)
|
||||
// 文件预上传
|
||||
func (uh uploadHandler) preUploadFile(c *gin.Context) {
|
||||
// md5,fileName,fileSize,cid
|
||||
// 检查MD5是否已存在
|
||||
// 检查名称是否合规安全
|
||||
// 检查文件大小是否超过限制
|
||||
// 检查文件类型是否符合要求
|
||||
|
||||
// 如果文件存在,复制到cid对应目录
|
||||
|
||||
}
|
||||
|
||||
// uploadVideo 上传视频
|
||||
func (uh uploadHandler) uploadVideo(c *gin.Context) {
|
||||
// uploadFile 上传文件
|
||||
func (uh uploadHandler) uploadFile(c *gin.Context) {
|
||||
var uReq commonSchema.CommonUploadImageReq
|
||||
if response.IsFailWithResp(c, util.VerifyUtil.VerifyBody(c, &uReq)) {
|
||||
return
|
||||
@@ -45,6 +43,6 @@ func (uh uploadHandler) uploadVideo(c *gin.Context) {
|
||||
if response.IsFailWithResp(c, ve) {
|
||||
return
|
||||
}
|
||||
res, err := commonService.UploadService.UploadVideo(file, uReq.Cid, config.AdminConfig.GetAdminId(c))
|
||||
res, err := commonService.UploadService.UploadFile(file, uReq.Cid, config.AdminConfig.GetAdminId(c))
|
||||
response.CheckAndRespWithData(c, res, err)
|
||||
}
|
||||
|
@@ -10,12 +10,13 @@ import (
|
||||
type Album struct {
|
||||
ID uint `gorm:"primarykey;comment:'主键ID'"`
|
||||
Cid uint `gorm:"not null;default:0;comment:'类目ID'"`
|
||||
Aid uint `gorm:"not null;default:0;comment:'管理ID'"`
|
||||
AdminId uint `gorm:"not null;default:0;comment:'管理员ID'"`
|
||||
Uid uint `gorm:"not null;default:0;comment:'用户ID'"`
|
||||
Type int `gorm:"not null;default:10;comment:'文件类型: [10=图片, 20=视频]''"`
|
||||
// Type int `gorm:"not null;default:10;comment:'文件类型: [10=图片, 20=视频]''"`
|
||||
Name string `gorm:"not null;default:'';comment:'文件名称''"`
|
||||
Uri string `gorm:"not null;comment:'文件路径'"`
|
||||
Ext string `gorm:"not null;default:'';comment:'文件扩展'"`
|
||||
Hash string `gorm:"not null;default:'';comment:'文件hash'"`
|
||||
Size int64 `gorm:"not null;default:0;comment:文件大小"`
|
||||
IsDelete soft_delete.DeletedAt `gorm:"not null;default:0;softDelete:flag,DeletedAtField:DeleteTime;comment:'是否删除: 0=否, 1=是'"`
|
||||
CreateTime core.NullTime `gorm:"autoCreateTime;not null;comment:'创建时间'"`
|
||||
@@ -26,8 +27,10 @@ type Album struct {
|
||||
// AlbumCate 相册分类实体
|
||||
type AlbumCate struct {
|
||||
ID uint `gorm:"primarykey;comment:'主键ID'"`
|
||||
AdminId uint `gorm:"not null;default:0;comment:'管理员ID'"`
|
||||
|
||||
Pid uint `gorm:"not null;default:0;comment:'父级ID'"`
|
||||
Type int `gorm:"not null;default:10;comment:'文件类型: [10=图片, 20=视频]''"`
|
||||
// Type int `gorm:"not null;default:10;comment:'文件类型: [10=图片, 20=视频]''"`
|
||||
Name string `gorm:"not null;default:'';comment:'分类名称''"`
|
||||
IsDelete soft_delete.DeletedAt `gorm:"not null;default:0;softDelete:flag,DeletedAtField:DeleteTime;comment:'是否删除: 0=否, 1=是'"`
|
||||
CreateTime core.NullTime `gorm:"autoCreateTime;not null;comment:'创建时间'"`
|
||||
|
@@ -20,7 +20,7 @@ var StorageDriver = storageDriver{}
|
||||
// UploadFile 文件对象
|
||||
type UploadFile struct {
|
||||
Name string // 文件名称
|
||||
Type int // 文件类型
|
||||
// Type int // 文件类型
|
||||
Size int64 // 文件大小
|
||||
Ext string // 文件扩展
|
||||
Uri string // 文件路径
|
||||
@@ -31,25 +31,34 @@ type UploadFile struct {
|
||||
type storageDriver struct{}
|
||||
|
||||
// Upload 根据引擎类型上传文件
|
||||
func (sd storageDriver) Upload(file *multipart.FileHeader, folder string, fileType int) (uf *UploadFile, e error) {
|
||||
func (sd storageDriver) Upload(file *multipart.FileHeader) (uf *UploadFile, e error) {
|
||||
// TODO: engine默认local
|
||||
if e = sd.checkFile(file, fileType); e != nil {
|
||||
|
||||
fileExt := sd.getFileExt(file.Filename)
|
||||
if fileExt == "" {
|
||||
return nil, response.AssertArgumentError.SetMessage("文件类型错误!")
|
||||
}
|
||||
|
||||
if e = sd.checkFile(file.Filename, file.Size); e != nil {
|
||||
return
|
||||
}
|
||||
key := sd.buildSaveName(file)
|
||||
var folder string = fileExt
|
||||
|
||||
saveName := sd.buildSaveName(file)
|
||||
engine := "local"
|
||||
if engine == "local" {
|
||||
if e = sd.localUpload(file, key, folder); e != nil {
|
||||
if e = sd.localUpload(file, folder, saveName); e != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
core.Logger.Errorf("storageDriver.Upload engine err: err=[unsupported engine]")
|
||||
return nil, response.Failed.SetMessage(fmt.Sprintf("engine:%s 暂时不支持", engine))
|
||||
}
|
||||
fileRelPath := path.Join(folder, key)
|
||||
|
||||
fileRelPath := path.Join(folder, saveName)
|
||||
return &UploadFile{
|
||||
Name: file.Filename,
|
||||
Type: fileType,
|
||||
// Type: int(fileType),
|
||||
Size: file.Size,
|
||||
Ext: strings.ToLower(strings.Replace(path.Ext(file.Filename), ".", "", 1)),
|
||||
Uri: fileRelPath,
|
||||
@@ -58,7 +67,7 @@ func (sd storageDriver) Upload(file *multipart.FileHeader, folder string, fileTy
|
||||
}
|
||||
|
||||
// localUpload 本地上传 (临时方法)
|
||||
func (sd storageDriver) localUpload(file *multipart.FileHeader, key string, folder string) (e error) {
|
||||
func (sd storageDriver) localUpload(file *multipart.FileHeader, folder string, saveName string) (e error) {
|
||||
// TODO: 临时方法,后续调整
|
||||
// 映射目录
|
||||
directory := config.FileConfig.UploadDirectory
|
||||
@@ -70,8 +79,8 @@ func (sd storageDriver) localUpload(file *multipart.FileHeader, key string, fold
|
||||
}
|
||||
defer src.Close()
|
||||
// 文件信息
|
||||
savePath := path.Join(directory, folder, path.Dir(key))
|
||||
saveFilePath := path.Join(directory, folder, key)
|
||||
savePath := path.Join(directory, folder, path.Dir(saveName))
|
||||
saveFilePath := path.Join(directory, folder, saveName)
|
||||
// 创建目录
|
||||
err = os.MkdirAll(savePath, 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
@@ -94,6 +103,7 @@ func (sd storageDriver) localUpload(file *multipart.FileHeader, key string, fold
|
||||
"storageDriver.localUpload Copy err: file=[%s], err=[%+v]", saveFilePath, err)
|
||||
return response.Failed.SetMessage("上传文件失败: " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -101,35 +111,40 @@ func (sd storageDriver) localUpload(file *multipart.FileHeader, key string, fold
|
||||
func (sd storageDriver) buildSaveName(file *multipart.FileHeader) string {
|
||||
name := file.Filename
|
||||
ext := strings.ToLower(path.Ext(name))
|
||||
date := time.Now().Format("20060201")
|
||||
date := time.Now().Format("20060102")
|
||||
return path.Join(date, util.ToolsUtil.MakeUuid()+ext)
|
||||
}
|
||||
|
||||
// checkFile 文件验证
|
||||
func (sd storageDriver) checkFile(file *multipart.FileHeader, fileType int) (e error) {
|
||||
fileName := file.Filename
|
||||
// getFileExt 获取文件扩展名
|
||||
func (sd storageDriver) getFileExt(fileName string) string {
|
||||
fileExt := strings.ToLower(strings.Replace(path.Ext(fileName), ".", "", 1))
|
||||
fileSize := file.Size
|
||||
switch fileType {
|
||||
case 10:
|
||||
// 图片文件
|
||||
if !util.ToolsUtil.Contains(config.FileConfig.UploadImageExt, fileExt) {
|
||||
return response.Failed.SetMessage("不被支持的图片扩展: " + fileExt)
|
||||
return fileExt
|
||||
}
|
||||
|
||||
// checkFile 文件验证
|
||||
func (sd storageDriver) checkFile(fileName string, fileSize int64) (e error) {
|
||||
|
||||
fileExt := sd.getFileExt(fileName)
|
||||
|
||||
if util.ToolsUtil.Contains(config.FileConfig.UploadImageExt, fileExt) {
|
||||
// 图片文件
|
||||
if fileSize > config.FileConfig.UploadImageSize {
|
||||
return response.Failed.SetMessage("上传图片不能超出限制: " + strconv.FormatInt(config.FileConfig.UploadImageSize/1024/1024, 10) + "M")
|
||||
}
|
||||
case 20:
|
||||
} else if util.ToolsUtil.Contains(config.FileConfig.UploadVideoExt, fileExt) {
|
||||
// 视频文件
|
||||
if !util.ToolsUtil.Contains(config.FileConfig.UploadVideoExt, fileExt) {
|
||||
return response.Failed.SetMessage("不被支持的视频扩展: " + fileExt)
|
||||
}
|
||||
if fileSize > config.FileConfig.UploadVideoSize {
|
||||
return response.Failed.SetMessage("上传视频不能超出限制: " + strconv.FormatInt(config.FileConfig.UploadVideoSize/1024/1024, 10) + "M")
|
||||
}
|
||||
default:
|
||||
} else if util.ToolsUtil.Contains(config.FileConfig.UploadFileExt, fileExt) {
|
||||
// 文件
|
||||
if fileSize > config.FileConfig.UploadFileSize {
|
||||
return response.Failed.SetMessage("上传文件不能超出限制: " + strconv.FormatInt(config.FileConfig.UploadFileSize/1024/1024, 10) + "M")
|
||||
}
|
||||
} else {
|
||||
core.Logger.Errorf("storageDriver.checkFile fileType err: err=[unsupported fileType]")
|
||||
return response.Failed.SetMessage("上传文件类型错误")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -9,8 +9,10 @@ type CommonUploadImageReq struct {
|
||||
//CommonAlbumListReq 相册文件列表参数
|
||||
type CommonAlbumListReq struct {
|
||||
Cid int `form:"cid,default=-1"` // 类目ID
|
||||
Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 文件类型: [10=图片, 20=视频]
|
||||
// Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 文件类型: [10=图片, 20=视频]
|
||||
Name string `form:"name"` // 文件名称
|
||||
Ext []string `form:"ext[]"` // 文件扩展
|
||||
|
||||
}
|
||||
|
||||
//CommonAlbumRenameReq 相册文件重命名参数
|
||||
@@ -28,13 +30,14 @@ type CommonAlbumMoveReq struct {
|
||||
//CommonAlbumAddReq 相册文件新增参数
|
||||
type CommonAlbumAddReq struct {
|
||||
Cid uint `form:"cid" binding:"gte=0"` // 类目ID
|
||||
Aid uint `form:"aid" binding:"gte=0"` // 管理ID
|
||||
Uid uint `form:"uid" binding:"gte=0"` // 用户ID
|
||||
Type int `form:"type" binding:"oneof=10 20 30"` // 文件类型: [10=图片, 20=视频,30文件]
|
||||
AdminId uint `form:"admin_id" binding:"gte=0"` // 管理ID
|
||||
// Uid uint `form:"uid" binding:"gte=0"` // 用户ID
|
||||
// Type int `form:"type" binding:"oneof=10 20 30"` // 文件类型: [10=图片, 20=视频,30文件]
|
||||
Name string `form:"name"` // 文件名称
|
||||
Uri string `form:"uri"` // 文件路径
|
||||
Ext string `form:"ext"` // 文件扩展
|
||||
Size int64 `form:"size"` // 文件大小
|
||||
Hash string `form:"hash"`
|
||||
}
|
||||
|
||||
//CommonAlbumDelReq 相册文件删除参数
|
||||
@@ -44,14 +47,14 @@ type CommonAlbumDelReq struct {
|
||||
|
||||
//CommonCateListReq 相册分类列表参数
|
||||
type CommonCateListReq struct {
|
||||
Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
|
||||
// Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
|
||||
Name string `form:"name"` // 分类名称
|
||||
}
|
||||
|
||||
//CommonCateAddReq 相册分类新增参数
|
||||
type CommonCateAddReq struct {
|
||||
Pid uint `form:"pid" binding:"gte=0"` // 父级ID
|
||||
Type int `form:"type" binding:"required,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
|
||||
// Type int `form:"type" binding:"required,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
|
||||
Name string `form:"name" binding:"required,min=1,max=30"` // 分类名称
|
||||
}
|
||||
|
||||
@@ -70,9 +73,9 @@ type CommonCateDelReq struct {
|
||||
type CommonUploadFileResp struct {
|
||||
ID uint `json:"id" structs:"id"` // 主键
|
||||
Cid uint `json:"cid" structs:"cid"` // 类目ID
|
||||
Aid uint `json:"aid" structs:"aid"` // 管理ID
|
||||
AdminId uint `json:"admin_id" structs:"admin_id"` // 管理ID
|
||||
Uid uint `json:"uid" structs:"uid"` // 用户ID
|
||||
Type int `json:"type" structs:"type"` // 文件类型: [10=图片, 20=视频]
|
||||
// Type int `json:"type" structs:"type"` // 文件类型: [10=图片, 20=视频]
|
||||
Name string `json:"name" structs:"name"` // 文件名称
|
||||
Uri string `json:"url" structs:"url"` // 文件路径
|
||||
Path string `json:"path" structs:"path"` // 访问地址
|
||||
|
@@ -14,22 +14,10 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type IAlbumService interface {
|
||||
AlbumList(page request.PageReq, listReq commonSchema.CommonAlbumListReq) (res response.PageResp, e error)
|
||||
AlbumRename(id uint, name string) (e error)
|
||||
AlbumMove(ids []uint, cid int) (e error)
|
||||
AlbumAdd(addReq commonSchema.CommonAlbumAddReq) (res uint, e error)
|
||||
AlbumDel(ids []uint) (e error)
|
||||
CateList(listReq commonSchema.CommonCateListReq) (mapList []commonSchema.CommonCateListResp, e error)
|
||||
CateAdd(addReq commonSchema.CommonCateAddReq) (e error)
|
||||
CateRename(id uint, name string) (e error)
|
||||
CateDel(id uint) (e error)
|
||||
}
|
||||
|
||||
var AlbumService = NewAlbumService()
|
||||
|
||||
// NewAlbumService 初始化
|
||||
func NewAlbumService() IAlbumService {
|
||||
func NewAlbumService() *albumService {
|
||||
db := core.GetDB()
|
||||
return &albumService{db: db}
|
||||
}
|
||||
@@ -40,21 +28,29 @@ type albumService struct {
|
||||
}
|
||||
|
||||
// AlbumList 相册文件列表
|
||||
func (albSrv albumService) AlbumList(page request.PageReq, listReq commonSchema.CommonAlbumListReq) (res response.PageResp, e error) {
|
||||
func (albSrv albumService) AlbumList(adminId uint, page request.PageReq, listReq commonSchema.CommonAlbumListReq) (res response.PageResp, e error) {
|
||||
|
||||
// 分页信息
|
||||
limit := page.PageSize
|
||||
offset := page.PageSize * (page.PageNo - 1)
|
||||
// 查询
|
||||
albumModel := albSrv.db.Model(&common_model.Album{}).Where("is_delete = ?", 0)
|
||||
|
||||
albumModel = albumModel.Where("admin_id = ?", adminId)
|
||||
|
||||
if listReq.Cid > 0 {
|
||||
albumModel = albumModel.Where("cid = ?", listReq.Cid)
|
||||
}
|
||||
if listReq.Name != "" {
|
||||
albumModel = albumModel.Where("name like ?", "%"+listReq.Name+"%")
|
||||
}
|
||||
if listReq.Type > 0 {
|
||||
albumModel = albumModel.Where("type = ?", listReq.Type)
|
||||
if len(listReq.Ext) > 0 {
|
||||
albumModel = albumModel.Where("ext in ?", listReq.Ext)
|
||||
}
|
||||
|
||||
// if listReq.Type > 0 {
|
||||
// albumModel = albumModel.Where("type = ?", listReq.Type)
|
||||
// }
|
||||
// 总数
|
||||
var count int64
|
||||
err := albumModel.Count(&count).Error
|
||||
@@ -161,12 +157,14 @@ func (albSrv albumService) AlbumDel(ids []uint) (e error) {
|
||||
}
|
||||
|
||||
// CateList 相册分类列表
|
||||
func (albSrv albumService) CateList(listReq commonSchema.CommonCateListReq) (mapList []commonSchema.CommonCateListResp, e error) {
|
||||
func (albSrv albumService) CateList(adminId uint, listReq commonSchema.CommonCateListReq) (mapList []commonSchema.CommonCateListResp, e error) {
|
||||
|
||||
var cates []common_model.AlbumCate
|
||||
cateModel := albSrv.db.Where("is_delete = ?", 0).Order("id desc")
|
||||
if listReq.Type > 0 {
|
||||
cateModel = cateModel.Where("type = ?", listReq.Type)
|
||||
}
|
||||
// if listReq.Type > 0 {
|
||||
// cateModel = cateModel.Where("type = ?", listReq.Type)
|
||||
// }
|
||||
cateModel = cateModel.Where("admin_id = ?", adminId)
|
||||
if listReq.Name != "" {
|
||||
cateModel = cateModel.Where("name like ?", "%"+listReq.Name+"%")
|
||||
}
|
||||
@@ -180,9 +178,12 @@ func (albSrv albumService) CateList(listReq commonSchema.CommonCateListReq) (map
|
||||
}
|
||||
|
||||
// CateAdd 分类新增
|
||||
func (albSrv albumService) CateAdd(addReq commonSchema.CommonCateAddReq) (e error) {
|
||||
func (albSrv albumService) CateAdd(adminId uint, addReq commonSchema.CommonCateAddReq) (e error) {
|
||||
|
||||
var cate common_model.AlbumCate
|
||||
convert_util.Copy(&cate, addReq)
|
||||
cate.AdminId = adminId
|
||||
|
||||
err := albSrv.db.Create(&cate).Error
|
||||
e = response.CheckErr(err, "Cate添加失败")
|
||||
return
|
||||
|
@@ -1,18 +1,16 @@
|
||||
package commonService
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"time"
|
||||
|
||||
"x_admin/plugin"
|
||||
"x_admin/schema/commonSchema"
|
||||
"x_admin/util"
|
||||
"x_admin/util/convert_util"
|
||||
)
|
||||
|
||||
type IUploadService interface {
|
||||
UploadImage(file *multipart.FileHeader, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error)
|
||||
UploadVideo(file *multipart.FileHeader, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error)
|
||||
}
|
||||
|
||||
var UploadService = NewUploadService()
|
||||
|
||||
// NewUploadService 初始化
|
||||
@@ -23,26 +21,34 @@ func NewUploadService() *uploadService {
|
||||
// uploadService 上传服务实现类
|
||||
type uploadService struct{}
|
||||
|
||||
// UploadImage 上传图片
|
||||
func (upSrv uploadService) UploadImage(file *multipart.FileHeader, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error) {
|
||||
return upSrv.uploadFile(file, "image", 10, cid, aid)
|
||||
}
|
||||
|
||||
// UploadVideo 上传视频
|
||||
func (upSrv uploadService) UploadVideo(file *multipart.FileHeader, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error) {
|
||||
return upSrv.uploadFile(file, "video", 20, cid, aid)
|
||||
}
|
||||
|
||||
// uploadFile 上传文件
|
||||
func (upSrv uploadService) uploadFile(file *multipart.FileHeader, folder string, fileType int, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error) {
|
||||
// UploadFile 上传
|
||||
// cid 分类id
|
||||
// AdminId 用户id
|
||||
func (upSrv uploadService) UploadFile(file *multipart.FileHeader, cid uint, AdminId uint) (res commonSchema.CommonUploadFileResp, e error) {
|
||||
var upRes *plugin.UploadFile
|
||||
if upRes, e = plugin.StorageDriver.Upload(file, folder, fileType); e != nil {
|
||||
if upRes, e = plugin.StorageDriver.Upload(file); e != nil {
|
||||
return
|
||||
}
|
||||
var startTime = time.Now()
|
||||
|
||||
// 计算文件MD5
|
||||
md5, e := util.ToolsUtil.GetFileMD5(file)
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
var endTime = time.Now()
|
||||
var costTime = endTime.UnixNano() - startTime.UnixNano()
|
||||
// 毫秒
|
||||
costTime = costTime / 1000000
|
||||
|
||||
fmt.Printf("\n文件大小%d , md5时间: %d毫秒\n", file.Size, costTime)
|
||||
|
||||
var addReq commonSchema.CommonAlbumAddReq
|
||||
convert_util.Copy(&addReq, upRes)
|
||||
addReq.Aid = aid
|
||||
addReq.Cid = cid
|
||||
addReq.AdminId = AdminId //管理员
|
||||
addReq.Cid = cid // 分类id
|
||||
addReq.Hash = md5
|
||||
|
||||
var albumId uint
|
||||
if albumId, e = AlbumService.AlbumAdd(addReq); e != nil {
|
||||
return
|
||||
|
@@ -4,8 +4,10 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -45,6 +47,20 @@ func (tu toolsUtil) MakeMd5(data string) string {
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// GetFileMD5 获取文件MD5
|
||||
func (tu toolsUtil) GetFileMD5(file *multipart.FileHeader) (string, error) {
|
||||
f, err := file.Open()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
hash := md5.New()
|
||||
if _, err := io.Copy(hash, f); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// MakeToken 生成唯一Token
|
||||
func (tu toolsUtil) MakeToken() string {
|
||||
ms := time.Now().UnixMilli()
|
||||
|
Reference in New Issue
Block a user