提交 7926c8ae authored 作者: 王鹏飞's avatar 王鹏飞

chore: 优化上传

上级 07b6e8b2
......@@ -30,6 +30,7 @@
"element-plus": "^2.8.7",
"eventsource-parser": "^3.0.2",
"file-saver": "^2.0.5",
"file-type": "^21.1.0",
"html-to-image": "^1.11.11",
"jspdf": "^2.5.1",
"lodash-es": "^4.17.21",
......@@ -138,6 +139,16 @@
"node": ">=6.9.0"
}
},
"node_modules/@borewit/text-codec": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz",
"integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/@bufbuild/protobuf": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.10.0.tgz",
......@@ -1584,6 +1595,36 @@
"vue": "^3.0.0"
}
},
"node_modules/@tokenizer/inflate": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.3.1.tgz",
"integrity": "sha512-4oeoZEBQdLdt5WmP/hx1KZ6D3/Oid/0cUb2nk4F0pTDAWy+KCH3/EnAkZF/bvckWo8I33EqBm01lIPgmgc8rCA==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.1",
"fflate": "^0.8.2",
"token-types": "^6.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/@tokenizer/inflate/node_modules/fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
"license": "MIT"
},
"node_modules/@tokenizer/token": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
"license": "MIT"
},
"node_modules/@tsconfig/node20": {
"version": "20.1.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz",
......@@ -4717,6 +4758,24 @@
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
},
"node_modules/file-type": {
"version": "21.1.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-21.1.0.tgz",
"integrity": "sha512-boU4EHmP3JXkwDo4uhyBhTt5pPstxB6eEXKJBu2yu2l7aAMMm7QQYQEzssJmKReZYrFdFOJS8koVo6bXIBGDqA==",
"license": "MIT",
"dependencies": {
"@tokenizer/inflate": "^0.3.1",
"strtok3": "^10.3.1",
"token-types": "^6.0.0",
"uint8array-extras": "^1.4.0"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
......@@ -8189,6 +8248,22 @@
"dev": true,
"license": "MIT"
},
"node_modules/strtok3": {
"version": "10.3.4",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz",
"integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==",
"license": "MIT",
"dependencies": {
"@tokenizer/token": "^0.3.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
......@@ -8534,6 +8609,24 @@
"node": ">=0.10.0"
}
},
"node_modules/token-types": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz",
"integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==",
"license": "MIT",
"dependencies": {
"@borewit/text-codec": "^0.1.0",
"@tokenizer/token": "^0.3.0",
"ieee754": "^1.2.1"
},
"engines": {
"node": ">=14.16"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/ts-api-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz",
......@@ -8695,6 +8788,18 @@
"source-map": "~0.6.1"
}
},
"node_modules/uint8array-extras": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz",
"integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
......
......@@ -37,6 +37,7 @@
"element-plus": "^2.8.7",
"eventsource-parser": "^3.0.2",
"file-saver": "^2.0.5",
"file-type": "^21.1.0",
"html-to-image": "^1.11.11",
"jspdf": "^2.5.1",
"lodash-es": "^4.17.21",
......
......@@ -13,6 +13,7 @@ const handleRefresh = () => {
}
const listParams = reactive({ name: '', live_commodity_type_id: '', live_commodity_title: '' })
const listLength = ref(0)
// 列表配置
const listOptions = computed(() => {
return {
......@@ -24,6 +25,10 @@ const listOptions = computed(() => {
params.live_commodity_type_id = listParams.live_commodity_type_id
return params
},
callback(data) {
listLength.value = data.list.length
return data
},
},
filters: [
{ label: '直播主题品类', prop: 'live_commodity_type_id', slots: 'filter-category' },
......@@ -77,7 +82,7 @@ const handleRemove = async (row) => {
<AppCard title="直播话术管理">
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" @click="handleAdd">添加话术</el-button>
<el-button type="primary" @click="handleAdd" v-if="!listLength">添加话术</el-button>
</template>
<template #filter-category>
<LiveProductCategory v-model="listParams.live_commodity_type_id" @change="handleRefresh"></LiveProductCategory>
......
......@@ -89,7 +89,10 @@ const {
onRecord: async (blob) => {
if (props.isLocalUpload) return
if (isOSSUpload) {
ossResult = await appendUpload(blob, ossResult?.nextAppendPosition, `${fileName.value}.mp4`)
ossResult = await appendUpload(blob, {
name: `live/${ssoId}/${fileName.value}.mp4`,
position: ossResult?.nextAppendPosition,
})
fileUrl.value = ossResult.url
} else {
const base64Data = await readBlobAsBase64(blob)
......
......@@ -50,7 +50,8 @@ export function useLive({ enabledUserMedia = true, onStart, onRecord, onStop }:
// 初始化MediaRecorder
const initializeMediaRecorder = () => {
if (!stream.value) return
mediaRecorder = new MediaRecorder(stream.value, { mimeType: 'video/webm' })
const mimeType = MediaRecorder.isTypeSupported('video/mp4') ? 'video/mp4' : 'video/webm'
mediaRecorder = new MediaRecorder(stream.value, { mimeType })
mediaRecorder.ondataavailable = handleDataAvailable
mediaRecorder.onstart = handleStart
mediaRecorder.onstop = handleStop
......@@ -93,7 +94,7 @@ export function useLive({ enabledUserMedia = true, onStart, onRecord, onStop }:
const start = () => {
if (!mediaRecorder) initializeMediaRecorder()
recordedChunks.value = []
mediaRecorder?.start(5000) // 每100ms触发一次dataavailable事件
mediaRecorder?.start(1000 * 10) // 每100ms触发一次dataavailable事件
}
// 停止录制
......
<script setup>
import AppEditor from '@/components/base/AppEditor.vue'
import { useFileDialog } from '@vueuse/core'
import { upload } from '@/utils/upload'
import { multipartUpload } from '@/utils/oss'
import { Delete } from '@element-plus/icons-vue'
const form = defineModel('form', {
......@@ -29,6 +29,7 @@ const emit = defineEmits(['save'])
const { open, onChange } = useFileDialog({
accept: 'image/*,video/*',
multiple: false,
})
const currentItem = computed(() => {
......@@ -46,16 +47,20 @@ const handleUpload = async () => {
}
}
const isLoading = ref(false)
const percentage = ref(0)
onChange(async (files) => {
if (!files) return
const [file] = files
isLoading.value = true
const res = await upload(file)
const res = await multipartUpload(file, {
progress: (progress) => {
percentage.value = Math.round(progress * 100)
},
})
if (currentItem.value.isImage) {
form.value[key.value] = form.value[key.value] ? [...form.value[key.value], res] : [res]
form.value[key.value] = form.value[key.value] ? [...form.value[key.value], res.url] : [res.url]
} else {
form.value[key.value] = res
form.value[key.value] = res.url
}
isLoading.value = false
})
......@@ -73,6 +78,7 @@ const handleDelete = (url) => {
<div style="background: #ecf2f7; padding: 40px; border-radius: 20px; min-height: 200px">
<template v-if="item.isImage || item.isVideo">
<el-button type="primary" round @click="handleUpload" :loading="isLoading">点击上传</el-button>
<el-progress :percentage="percentage" v-if="isLoading" />
<div v-if="item.isImage" class="upload-tips">
<p>图片格式为png/jpg/jpeg,图片大小不能超过2M</p>
</div>
......
import OSS from 'ali-oss'
import md5 from 'blueimp-md5'
import { fileTypeFromBlob } from 'file-type'
import { getToken } from '@/api/base'
export const prefix = 'upload/saas-dml-live/'
let store: OSS | null = null
export async function getTokenInfo() {
......@@ -30,33 +29,35 @@ export async function createStore() {
}))
}
export function generateObjectName(file: Blob | File, objectName?: string) {
if (objectName) return `${prefix}${objectName}`
let fileType = 'png'
if (file instanceof File && file.name) {
const matches = file.name.match(/\.(\w+)$/)
if (matches) {
fileType = matches[1]
}
} else if (file.type) {
const mimeType = file.type.split('/').pop()
if (mimeType) {
fileType = mimeType
}
export async function parseParams(file: Blob | File, options: any = {}) {
const { name, prefix = 'upload/saas-dml-pro/', ...rest } = options
let objectName: string
if (name) {
objectName = `${prefix}${name}`
} else {
const result = await fileTypeFromBlob(file)
objectName = `${prefix}${md5(new Date().getTime() + Math.random().toString(36).slice(-8))}.${result?.ext}`
}
return `${prefix}${md5(new Date().getTime() + Math.random().toString(36).slice(-8))}.${fileType}`
return { name: objectName, file, options: rest }
}
// 上传
export async function putUpload(file: Blob | File, objectName?: string) {
export async function putUpload(file: Blob | File, options?: any) {
const store = await createStore()
return store.put(generateObjectName(file, objectName), file)
const params = await parseParams(file, options)
return store.put(params.name, params.file, params.options)
}
// 追加上传
export async function appendUpload(file: Blob | File, position = '', objectName?: string) {
export async function appendUpload(file: Blob | File, options?: any) {
const store = await createStore()
const params = await parseParams(file, options)
return store.append(params.name, params.file, params.options)
}
export async function multipartUpload(file: Blob | File, options?: any) {
const store = await createStore()
let options = {}
if (position) options = { position }
return store.append(generateObjectName(file, objectName), file, options)
const params = await parseParams(file, options)
const res: any = await store.multipartUpload(params.name, params.file, params.options)
return { url: `https://webapp-pub.ezijing.com/${res.name}`, ...res }
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论