This commit is contained in:
c
2025-03-12 20:11:03 +08:00
commit 70b7c82c97
1178 changed files with 148185 additions and 0 deletions
@@ -0,0 +1,15 @@
export function generateCoverURL (url, cropRule) {
if (!url || !cropRule) return url
const parsedUrl = new URL(url)
if (parsedUrl.pathname.endsWith('.webp')) {
return url
}
const isQuery = parsedUrl.search.length > 0
const separator = isQuery ? '&' : '?'
return url + separator + cropRule
}
@@ -0,0 +1,103 @@
async function getImageInfo(tempFile, fileInfo) {
return new Promise(resolve => {
uni.getImageInfo({
src: fileInfo.src,
success: (res) => {
fileInfo.width = res.width
fileInfo.height = res.height
},
complete: () => {
resolve()
}
})
})
}
async function getVideoInfo(tempFile, fileInfo) {
return new Promise(resolve => {
// #ifdef H5
if (!('duration' in tempFile)) {
const video = document.createElement('video')
video.src = tempFile.path
// 获取视频的长度/时间
video.addEventListener('loadedmetadata', () => {
fileInfo.duration = video.duration
fileInfo.width = video.videoWidth
fileInfo.height = video.videoHeight
resolve()
})
return
}
// #endif
fileInfo.duration = tempFile.duration
fileInfo.width = tempFile.width
fileInfo.height = tempFile.height
resolve()
})
}
export async function transformPreviewMediaList(tempFiles = []) {
const files = []
for (const tempFile of tempFiles) {
const [mediaType = '', fileType] = (tempFile.type || tempFile.fileType)?.split('/') || []
const fileInfo = {
src: tempFile.path,
size: tempFile.size,
type: mediaType,
fileType: fileType,
name: tempFile.name
}
if (mediaType === 'image') {
await getImageInfo(tempFile, fileInfo)
} else if (mediaType === 'video') {
await getVideoInfo(tempFile, fileInfo)
}
files.push(fileInfo)
}
return files
}
const imageExtname = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
const videoExtname = ['.mp4', '.avi', '.mov', '.rmvb', '.rm', '.flv', '.3gp', '.wmv', '.mkv']
export const extnameMap = {
all: [...imageExtname, ...videoExtname],
image: imageExtname,
video: videoExtname,
}
export function cropImg(file) {
return new Promise((resolve, reject) => {
let ext
let filePathProcessed = file.path // 处理结果
// #ifdef H5
ext = file.name.split('.').pop()
resolve({
path: filePathProcessed,
ext,
fileType: file.fileType
})
// #endif
// #ifndef H5
uni.getImageInfo({
src: file.path,
success(info) {
ext = info.type.toLowerCase()
resolve({
path: filePathProcessed,
ext,
fileType: file.fileType
})
},
fail(err) {
reject(new Error(err.errMsg || '未能获取图片类型'))
}
})
// #endif
})
}
@@ -0,0 +1,61 @@
const uniMediaLibraryCo = uniCloud.importObject('uni-media-library-co', {
customUI: true
})
export default class UploadURL {
constructor(props) {
this.provider = props.provider
this.images = props.images
this.$mediaList = props.mediaList
}
get preUploadImages () {
return this.images.map(item => {
return {
src: item.thumbUrl,
raw: item.url,
// size: tempFile.size,
type: 'image',
// fileType: fileType,
name: item.originalName,
width: item.width,
height: item.height,
attributes: {
description: item.description,
alt: item.alt
}
}
})
}
async upload () {
if (!this.images || !this.images.length) return false
this.$mediaList.chooseFiles(
this.preUploadImages,
)
const promises = this.preUploadImages.map((item, index) => {
this.$mediaList.mediaUploadProgress({
index,
loaded: 0,
total: 0,
tempFilePath: item.src
})
return uniMediaLibraryCo.uploadImage({
url: item.raw
}).then(res => {
return {
path: item.src,
url: res.data.fileId,
attributes: item.attributes
}
})
})
await this.$mediaList.mediaUploadSuccess(
await Promise.all(promises)
)
}
}
@@ -0,0 +1,62 @@
import createUploaderProviderInstance from './provider'
class Uploader {
constructor(props) {
props = Object.assign({
provider: 'internal'
}, props)
this.uploaderInstance = createUploaderProviderInstance(props.provider)
this.imageLibraryProvider = props.imageLibraryProvider
this.callbacks = {}
this.waitUploadFiles = []
this.uploaderInstance.on('progress', (data) => {
this.emit('progress', data)
})
this.uploaderInstance.on('success', (data) => {
this.emit('success', data)
})
this.uploaderInstance.on('error', (data) => {
this.emit('error', data)
})
}
on (event, callback) {
this.callbacks[event] = callback
}
emit (event, data) {
if (this.callbacks[event]) {
this.callbacks[event](data)
}
}
async add (file) {
if (file instanceof Array) {
const files = await this.normalizeFiles(file)
this.waitUploadFiles = this.waitUploadFiles.concat(files)
} else {
this.waitUploadFiles.push(
await this.normalizeFiles([file])
)
}
}
async upload () {
return this.uploaderInstance.upload(this.waitUploadFiles)
}
async cloudUpload () {
return this.uploaderInstance.cloudUpload(this.waitUploadFiles, {
imageLibraryProvider: this.imageLibraryProvider
})
}
normalizeFiles (files) {
return Promise.resolve(files)
}
}
export default Uploader
@@ -0,0 +1,154 @@
import Uploader from "./base";
import UploaderFile from "./struct/upload-file";
export class FromFileUploader extends Uploader {
constructor(props) {
super(props)
}
chooseImage (options) {
return new Promise((resolve, reject) => {
uni.chooseImage({
sizeType: ['original'],
extension: options.extension,
success: (res) => {
resolve(res.tempFiles)
},
fail: (e) => {
reject(e)
}
})
})
}
chooseVideo (options) {
return new Promise((resolve, reject) => {
uni.chooseVideo({
compressed: false,
extension: options.extension,
success: (res) => {
resolve([
{
name: (res.tempFile && res.tempFile.name) || 'unknown',
type: (res.tempFile && res.tempFile.type) || '',
path: res.tempFilePath,
duration: res.duration,
size: res.size,
height: res.height,
width: res.width,
}
])
},
fail: (e) => {
reject(e)
}
})
})
}
chooseAll (options) {
return new Promise((resolve, reject) => {
uni.chooseFile({
type: 'all',
extension: options.extension,
success: (res) => {
resolve(res.tempFiles)
},
fail: (e) => {
reject(e)
}
})
})
}
async chooseFileAndUpload (options) {
const {type, onChooseFile, success, fail} = options
let choosePromise
if (type === 'image') {
choosePromise = this.chooseImage(options)
} else if (type === 'video') {
choosePromise = this.chooseVideo(options)
} else {
choosePromise = this.chooseAll(options)
}
return choosePromise.then(files => {
return this.add(files)
}).then(() => {
typeof onChooseFile === 'function' && onChooseFile(
this.waitUploadFiles
)
return this.upload()
}).then(success).catch(fail)
}
getImageInfo(tempFile, fileInfo) {
return new Promise(resolve => {
uni.getImageInfo({
src: fileInfo.originUrl,
success: (res) => {
fileInfo.attributes.width = res.width
fileInfo.attributes.height = res.height
},
complete: () => {
resolve()
}
})
})
}
getVideoInfo(tempFile, fileInfo) {
return new Promise(resolve => {
// #ifdef H5
if (!('duration' in tempFile)) {
const video = document.createElement('video')
video.src = fileInfo.originUrl
// 获取视频的长度/时间
video.addEventListener('loadedmetadata', () => {
fileInfo.attributes.duration = video.duration
fileInfo.attributes.width = video.videoWidth
fileInfo.attributes.height = video.videoHeight
resolve()
})
return
}
// #endif
fileInfo.attributes.duration = tempFile.duration
fileInfo.attributes.width = tempFile.width
fileInfo.attributes.height = tempFile.height
resolve()
})
}
async normalizeFiles (files) {
let newFiles = []
for (const file of files) {
const [mediaType = '', fileType] = (file.type || file.fileType)?.split('/') || []
const fileInfo = {
name: file.name,
originUrl: file.path,
thumbUrl: file.path,
size: file.size,
mediaType: mediaType,
fileType: fileType,
attributes: {}
}
if (mediaType === 'image') {
await this.getImageInfo(file, fileInfo)
} else if (mediaType === 'video') {
await this.getVideoInfo(file, fileInfo)
}
newFiles.push(
new UploaderFile(fileInfo)
)
}
return newFiles
}
}
@@ -0,0 +1,2 @@
export * from './file'
export * from './url'
@@ -0,0 +1,17 @@
class UploadProvider {
constructor() {
this.callbacks = {}
}
on (event, callback) {
this.callbacks[event] = callback
}
emit (event, data) {
if (this.callbacks[event]) {
this.callbacks[event](data)
}
}
}
export default UploadProvider
@@ -0,0 +1,14 @@
import InternalUploadProvider from './internal'
import QiniuUploadProvider from './qiniu'
function createUploaderProviderInstance (provider) {
switch (provider) {
case 'internal':
return new InternalUploadProvider()
case 'qiniu':
return new QiniuUploadProvider()
default:
return new InternalUploadProvider()
}
}
export default createUploaderProviderInstance
@@ -0,0 +1,81 @@
import BaseProvider from './base';
const uniMediaLibraryCo = uniCloud.importObject('uni-media-library-co', {
customUI: true
})
class InternalUploadProvider extends BaseProvider {
async upload (files) {
for (let i = 0; i < files.length; i++) {
const file = files[i]
const ext = await file.getFileType()
try {
const result = await uniCloud.uploadFile({
filePath: file.originUrl,
fileType: file.mediaType,
cloudPathAsRealPath: true,
cloudPath: `uni-media-library/${Date.now()}.${ext}`,
onUploadProgress: (progressEvent) => {
file.setStatus('uploading')
file.setPercent(Math.floor((progressEvent.loaded / progressEvent.total) * 100))
this.emit('progress', file)
}
})
file.setStatus('uploaded')
file.setUrl(result.fileID)
file.setPercent(100)
} catch (e) {
file.setStatus('failed')
file.setError(e)
console.error(e)
}
if ((i + 1) === files.length) {
this.emit('success', files)
}
}
}
async cloudUpload (files, options) {
const { imageLibraryProvider } = options
for (let i = 0; i < files.length; i++) {
const file = files[i]
try {
file.setStatus('uploading')
file.setPercent(0)
this.emit('progress', file)
const result = await uniMediaLibraryCo.uploadImage({
url: file.originUrl,
id: file.id,
provider: imageLibraryProvider
})
if (!result.data.fileID) {
file.setStatus('failed')
file.setError(new Error('上传失败'))
this.emit('error', file)
return
}
file.setStatus('uploaded')
file.setUrl(result.data.fileID)
file.setPercent(100)
file.setAttributes(result.data.detail)
} catch (e) {
file.setStatus('failed')
file.setError(e)
console.error(e)
this.emit('error', file)
}
if ((i + 1) === files.length) {
this.emit('success', files)
}
}
}
}
export default InternalUploadProvider
@@ -0,0 +1,5 @@
import BaseProvider from './base';
class QiniuUploadProvider extends BaseProvider {}
export default QiniuUploadProvider
@@ -0,0 +1,75 @@
class UploaderFile {
constructor(props = {}) {
if (!props.name || !props.mediaType || !props.originUrl) {
throw new Error('缺少必要字段')
}
this.id = props.id // id
this.name = props.name // originalName
this.size = props.size // fileSize
this.mediaType = props.mediaType // type
this.fileType = props.fileType // fileType
this.originUrl = props.originUrl // url
this.thumbUrl = props.thumbUrl // thumbUrl
this.attributes = props.attributes || {} // description, alt
this.url = '' // fileId
this.status = 'waiting' // waiting, uploading, uploaded, failed
this.percent = 0 // 0-100
this.error = null
}
setError (error) {
this.error = error
}
setUrl (url) {
this.url = url
}
setPercent (percent) {
this.percent = percent
}
setStatus (status) {
this.status = status
}
setAttributes (attributes = {}) {
if (attributes.fileType) {
this.fileType = attributes.fileType
}
if (attributes.size) {
this.size = attributes.size
}
this.attributes = attributes
}
getFileType () {
return new Promise((resolve, reject) => {
if (this.fileType) {
return resolve(this.fileType)
}
// #ifdef H5
this.fileType = this.name.split('.').pop()
resolve(this.fileType)
// #endif
// #ifndef H5
uni.getImageInfo({
src: this.originUrl,
success(info) {
this.fileType = info.type.toLowerCase()
resolve(this.fileType)
},
fail(err) {
reject(new Error(err.errMsg || '未能获取图片类型'))
}
})
// #endif
})
}
}
export default UploaderFile
@@ -0,0 +1,20 @@
import Uploader from './base';
import UploaderFile from "./struct/upload-file";
export class FromURLUploader extends Uploader {
constructor(props) {
super(props);
}
normalizeFiles(files) {
return files.map(file => {
return new UploaderFile({
id: file.id,
name: file.name,
originUrl: file.url,
thumbUrl: file.thumbUrl || file.url,
mediaType: file.mediaType
})
})
}
}