Skip to main content

小程序版

参考项目:凤娱指数/凤向标

fileUpload

import UploadWx from "@components/uploadWx";
import FileUpload from "./uploadComponent";

// 高阶组件
export default UploadWx(FileUpload);

uploadComponent

import React, { Component } from 'react'
import { View } from '@tarojs/components'
import Taro from '@tarojs/taro'

/*
album:从相册选图
camera:使用相机
user:使用前置摄像头(仅H5纯浏览器使用)
environment:使用后置摄像头(仅H5纯浏览器)
*/

// UA
const userAgent = navigator.userAgent.toLocaleLowerCase()

// 判断小程序环境
export const isWeapp = () => {
const env = Taro.getEnv().toLocaleLowerCase()
if (env === 'weapp') {
return true
} else {
return false
}
}

// 外部参数类型(外部props和高阶函数)
interface IProps {
getUploadHandle: (slides: Array<Object>) => void
deleteServerImage: (index: Number) => void
deleteServerVideo: (index: Number) => void
choose: (type: Array<Object>, img?: Boolean) => void // 选择相册图片
deleteImg: (index: Number, type: Number) => void // 删除图片
imgPath: Array<Object>
VideoPath: Array<Object>
uploadImgPathArray: Array<Object>
changeState?: () => void
events?: any
secretImgPath: Array<Object>
secretVideoPath: Array<Object>
isStarUpload: Boolean
saveData?: any
}

interface PageState {
secretImgPath: Array<Object>
isAndriodUCbrowser: Boolean
imgKeyPath: Array<Object>
}

class FileUpload extends Component<IProps, PageState> {
state = {
secretImgPath: [],
imgKeyPath: [],
isAndriodUCbrowser:
(userAgent.match(/ucbrowser/) || userAgent.match(/ u;/)) &&
userAgent.includes('android') &&
!userAgent.match(/miuibrowser/) &&
!userAgent.match(/vivobrowser/) &&
!userAgent.match(/oppo/) &&
!userAgent.match(/mqqbrowser/) &&
!userAgent.match(/heytapbrowser/),
}

uploadTimer = null

static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.secretImgPath !== prevState.secretImgPath) {
return {
isOpenFileUpFixed: prevState.isOpenFileUpFixed,
secretImgPath: nextProps.secretImgPath,
secretVideoPath: nextProps.secretVideoPath,
}
}
return null
}

componentDidMount() {
// 高阶函数
const {
getUploadHandle,
events // 高阶事件监听
} = this.props

// 事件监听:图片过大
events.on('hasMaxFile', () => {
Taro.showToast({
title: '您上传的图片超过5M或者视频超过100M',
icon: 'none',
duration: 2000,
})
})
// 事件监听:上传中(时间过长自动断开)
events.on('starUpload', () => {
Taro.showLoading({
title: '上传中',
mask: true,
})

clearTimeout(this.uploadTimer)
this.uploadTimer = setTimeout(() => {
Taro.hideLoading()
Taro.showToast({
title: '上传已超时,请重新上传',
icon: 'none',
duration: 1000,
})
}, 3000)
})

// 事件监听:上传结束
events.on('endUpload', () => {
clearTimeout(this.uploadTimer)
Taro.hideLoading()
})

// 事件监听:获取本地化数据
events.on(
'uploadFinish',
(uploadImgPathArray, failArray) => {

if (failArray && failArray.length > 0) {
Taro.showToast({
title: '您上传的文件失败,请重新上传!',
icon: 'none',
duration: 1000,
})
}
// 仅获取最新一张
const newUploadImgPathArray = uploadImgPathArray.slice(-1)
console.log('uploadImgPathArray', newUploadImgPathArray);

// 图片URL[]
const imgKeyPath = []

newUploadImgPathArray.map(item => {
const imgKey =
(item &&
item.split('/') &&
item.split('/').pop() &&
item.split('/').pop().split('.')[0]) ||
''

if (imgKey) {
imgKeyPath.push(imgKey)
}
})

this.setState({
imgKeyPath,
})

getUploadHandle(newUploadImgPathArray)
}
)
}

changeShowStatus = () => {
const { isOpenFileUpFixed } = this.state

this.setState({ isOpenFileUpFixed: !isOpenFileUpFixed })
}

// 上传图片:调用高阶函数
fileUploadType = async (type) => {
// 类型为图片集合
if (type === 'slides') {
if (isWeapp()) {
// 小程序
this.props.choose(['album', 'camera'], 1, 5242880)
} else {
// 端外
this.props.choose(['album'], 1, 5242880)
}
}
}

render() {
return (
<View
className={styles.attachmentButton}
onClick={this.fileUploadType.bind(this, 'slides')}
/>
)
}
}

export default FileUpload

UploadWx

import Core from '@ifeng/taro_upload_core'
import React, { Component } from 'react'
import Taro, { Events } from '@tarojs/taro'
import { View } from '@tarojs/components'
// 手写Promise.allSettled:捕获全部状态
import { all_settled } from '@utils/utils/util'

// 用户token
let token = Taro.getStorageSync('token')

interface sourceType {
/** 从相册选图 */
album
/** 使用相机 */
camera
/** 使用前置摄像头(仅H5纯浏览器使用) */
user
/** 使用后置摄像头(仅H5纯浏览器) */
environment
}

const UploadWx = WrappedComponent => {
return class T extends Component {
state = {
imgPath: [],
uploadImgPathArray: [], // 上传图片集合
processArray: [],
}
//
events = new Events()
uploadCore = null

// 初始服务器(腾讯桶)
componentDidMount() {
this.uploadCore = new Core({
appid: 'ifeng-wind-vane', // 凤娱指数:腾讯桶
token: token,
apiUrl: 'https://skserver.ifeng.com/cos/getCredential',
initFail: function (err: any) {
console.error('error', err)
throw new Error(err)
},
})
}
// 选择图片
choose = async (
type: Array<keyof sourceType>,
count?: number,
size?: number
) => {
try {
// Taro上传图片
let res = await Taro.chooseImage({
count: count || 9, // 最多可以选择的图片张数
sourceType: [...type],
})

const { tempFiles, tempFilePaths = [] } = res
console.log('tempFiles',tempFiles);

// 体积限制
if (size) {
if (this.isHasMaxFile(tempFiles, size)) {
return false
}
}

this.uploadAll( tempFiles, tempFilePaths)
} catch (error) {
console.log(error)
}
}
// 限制上传文件大小
isHasMaxFile = (tempFiles, limitSize) => {
if (!tempFiles) return false
for (let i = 0, len = tempFiles.length; i < len; i++) {
const iItem = (tempFiles[i] && tempFiles[i].size) || 0
const maxFileSize = limitSize || 104857600
if (iItem > maxFileSize) {
this.events.trigger('hasMaxFile')
return true
}
}

return false
}
// 图片上传服务
uploadAll = async (tempFiles, tempFilePaths) => {

try {
// 开始上传事件
this.events.trigger('starUpload')
const promiseArr = tempFiles.map(item => {
console.log(item.size)
return new Promise((resolve, reject) => {
try {
if (item.size > 10 * 1024 * 1024) {
this.uploadCore.uploadBigFile({
file: {
filePath: item.path,
size: item.size,
},
success: function (res) {
resolve(res?.sourcePath)
},
error: function (err) {
console.error('上传出错啦', err)
reject(0)
},
})
} else {
console.log('开始上传事件');
this.uploadCore.uploadFile({
file: {
filePath: item.path,
size: item.size,
},
success: function (res) {
resolve(res.sourcePath)
},
error: function (err) {
console.error('上传出错啦', err)
reject(0)
},
})
}
} catch (error) {
console.log(error)
reject(0)
}
})
})

// 获取所有图片上传状态
const resArr = await all_settled(promiseArr)
// 成功
const uploadSucess = []
// 失败
const uploadError = []

resArr.forEach((item, index) => {
const { value } = item
if (value) {
uploadSucess.push(tempFilePaths[index])
} else {
uploadError.push(tempFilePaths[index])
}
})

// 服务器图片地址
let uploadUrlSucessImg = this.state.uploadImgPathArray
// 本地图片地址
let imgPath = this.state.imgPath

uploadUrlSucessImg = uploadUrlSucessImg.concat(
resArr.filter(item => item.value).map(item => item.value)
)
imgPath = imgPath.concat(uploadSucess)

this.setState({
imgPath,
uploadImgPathArray: uploadUrlSucessImg,
})

// 事件触发:完成上传传递参数
this.events.trigger(
'uploadFinish',
uploadUrlSucessImg,
uploadError
)
this.events.trigger('endUpload')
} catch (error) {
console.log(error)
}
}
deleteImg = (index, type = 1) => {
// 1 图片, 2 视频
if (type === 1) {
// 删除指定图片
let imgPath = this.state.imgPath
let uploadImgPathArray = this.state.uploadImgPathArray
imgPath.splice(index, 1)
uploadImgPathArray.splice(index, 1)
this.setState({
imgPath,
uploadImgPathArray,
})
}
}
render() {
let {
uploadImgPathArray,
imgPath,
processArray,
} = this.state
return (
<View>
<WrappedComponent
choose={this.choose}
imgPath={imgPath}
uploadImgPathArray={uploadImgPathArray}
deleteImg={this.deleteImg}
processArray={processArray}
events={this.events}
{...this.props}
/>
</View>
)
}
}
}

export default UploadWx