提交 ced3159e 编写于 作者: D DCloud_LXH

feat(h5): chooseFile

上级 f34dc72f
......@@ -189,7 +189,7 @@ export function defineOffApi<T extends ApiLike>(
export function defineTaskApi<T extends TaskApiLike, P = AsyncApiOptions<T>>(
name: string,
fn: (
args: Omit<P, 'success' | 'fail' | 'complete'>,
args: Omit<P, CALLBACK_TYPES>,
res: {
resolve: (res?: AsyncApiRes<P>) => void
reject: (err?: string) => void
......
import {
CHOOSE_SOURCE_TYPES,
elemsInArray,
elemInArray,
} from '../../helpers/protocol'
export const API_CHOOSE_FILE = 'chooseFile'
export type API_TYPE_CHOOSE_FILE = typeof uni.chooseFile
export type API_TYPE_CHOOSE_FILE_OPTIONS = AsyncApiOptions<API_TYPE_CHOOSE_FILE>
const CHOOSE_MEDIA_TYPE: API_TYPE_CHOOSE_FILE_OPTIONS['type'][] = [
'all',
'image',
'video',
]
export const ChooseFileOptions: ApiOptions<API_TYPE_CHOOSE_FILE> = {
formatArgs: {
count(count, params) {
if (count! <= 0) {
params.count = 100
}
},
sourceType(sourceType, params) {
params.sourceType = elemsInArray(sourceType, CHOOSE_SOURCE_TYPES)
},
type(type, params) {
params.type = elemInArray(type, CHOOSE_MEDIA_TYPE)
},
extension(extension, params) {
if (extension instanceof Array && extension.length === 0) {
return 'param extension should not be empty.'
}
if (!extension) params.extension = ['']
},
},
}
export const ChooseFileProtocol: ApiProtocol<API_TYPE_CHOOSE_FILE> = {
count: Number,
sourceType: Array,
type: String as any,
extension: Array,
}
const MIMEType: {
image: Record<string, any>
video: Record<string, any>
} = {
/**
* 关于图片常见的MIME类型
*/
image: {
jpg: 'jpeg',
jpe: 'jpeg',
pbm: 'x-portable-bitmap',
pgm: 'x-portable-graymap',
pnm: 'x-portable-anymap',
ppm: 'x-portable-pixmap',
psd: 'vnd.adobe.photoshop',
pic: 'x-pict',
rgb: 'x-rgb',
svg: 'svg+xml',
svgz: 'svg+xml',
tif: 'tiff',
xif: 'vnd.xiff',
wbmp: 'vnd.wap.wbmp',
wdp: 'vnd.ms-photo',
xbm: 'x-xbitmap',
ico: 'x-icon',
},
/**
* 关于视频常见的MIME类型
*/
video: {
'3g2': '3gpp2',
'3gp': '3gpp',
avi: 'x-msvideo',
f4v: 'x-f4v',
flv: 'x-flv',
jpgm: 'jpm',
jpgv: 'jpeg',
m1v: 'mpeg',
m2v: 'mpeg',
mpe: 'mpeg',
mpg: 'mpeg',
mpg4: 'mpeg',
m4v: 'x-m4v',
mkv: 'x-matroska',
mov: 'quicktime',
qt: 'quicktime',
movie: 'x-sgi-movie',
mp4v: 'mp4',
ogv: 'ogg',
smv: 'x-smv',
wm: 'x-ms-wm',
wmv: 'x-ms-wmv',
wmx: 'x-ms-wmx',
wvx: 'x-ms-wvx',
},
}
export default MIMEType
//#region imp functions
import {
API_CHOOSE_FILE,
ChooseFileOptions,
ChooseFileProtocol,
defineAsyncApi,
} from '@dcloudio/uni-api'
import { fileToUrl } from '../../../helpers/file'
import _createInput from './createInput'
//#endregion
//#region types
import type { API_TYPE_CHOOSE_FILE } from '@dcloudio/uni-api'
type TempFile = UniApp.ChooseFileSuccessCallbackResultFile
//#endregion
let fileInput: HTMLInputElement = null as any
export const chooseFile = defineAsyncApi<API_TYPE_CHOOSE_FILE>(
API_CHOOSE_FILE,
(
{
// sizeType,
count,
sourceType,
type,
extension,
},
{ resolve, reject }
) => {
// TODO handle sizeType 尝试通过 canvas 压缩
if (fileInput) {
document.body.removeChild(fileInput)
fileInput = null as any
}
fileInput = _createInput({
count,
sourceType,
type,
extension,
})
document.body.appendChild(fileInput)
fileInput.addEventListener('change', function (event: Event) {
const eventTarget = event.target as HTMLInputElement
const tempFiles: TempFile[] = []
if (eventTarget && eventTarget.files) {
const fileCount = eventTarget.files.length
for (let i = 0; i < fileCount; i++) {
const file = eventTarget.files[i]
let filePath: string
Object.defineProperty(file, 'path', {
get() {
filePath = filePath || fileToUrl(file)
return filePath
},
})
if (i < count!) tempFiles.push(file as any)
}
}
const res = {
errMsg: 'chooseFile:ok',
get tempFilePaths() {
return tempFiles.map(({ path }) => path)
},
tempFiles: tempFiles,
}
resolve(res)
// TODO 用户取消选择时,触发 fail,目前尚未找到合适的方法。
})
fileInput.click()
},
ChooseFileProtocol,
ChooseFileOptions
)
import { updateElementStyle } from '@dcloudio/uni-shared'
import MIMEType from './MIMEType'
export type createInputOptions = Pick<
UniApp.ChooseFileOptions,
'count' | 'sourceType' | 'type' | 'extension'
>
const ALL = 'all'
function isWXEnv(): boolean {
const ua = window.navigator.userAgent.toLowerCase()
const matchUA = ua.match(/MicroMessenger/i)
return !!(matchUA && matchUA[0] === 'micromessenger')
}
export default function ({
count,
sourceType,
type,
extension,
}: createInputOptions): HTMLInputElement {
const inputEl = document.createElement('input')
inputEl.type = 'file'
updateElementStyle(inputEl, {
position: 'absolute',
visibility: 'hidden',
zIndex: '-999',
width: '0',
height: '0',
top: '0',
left: '0',
})
/**
* 选择文件
* chooseFile 使用后缀名
* chooseImage、chooseVideo 使用MIME类型
*/
inputEl.accept = extension!
.map((item) => {
if (type !== ALL) {
const MIMEKey = item.replace('.', '')
return `${type}/${MIMEType[type!][MIMEKey] || MIMEKey}`
} else {
// 在微信环境里,'.jpeg,.png' 会提示没有应用可执行此操作
if (isWXEnv()) {
return '.'
}
return item.indexOf('.') === 0 ? item : `.${item}`
}
})
.join(',')
if (count && count > 1) {
inputEl.multiple = true
}
// 经过测试,仅能限制只通过相机拍摄,不能限制只允许从相册选择。
if (
type !== ALL &&
sourceType instanceof Array &&
sourceType.length === 1 &&
sourceType[0] === 'camera'
) {
inputEl.setAttribute('capture', 'camera')
}
return inputEl
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册