React Native Expo项目上传文件
Index.js:
import React from 'react'
import {
ScrollView,
View,
TouchableWithoutFeedback,
Text,
Image,
} from 'react-native'
import { WebView } from 'react-native-webview'
import style from './style.js'
import { Icon } from '../../../component/light'
import { Divider, LinearProgress, Button, Input, Dialog } from '@rneui/themed'
import useList from './useList.js'
export default function Index(props) {
const {
username,
emailCode,
isSendEmail,
count,
avatar,
nickname,
password,
code,
visible,
isLoading,
captcha,
visible1,
usernameEl,
setEmailCode,
handleSendEmail,
setNickname,
setPassword,
setCode,
toggleDialog1,
handleInput,
handleLogin,
handleNav,
handleVisilbe,
handleUploadAvatar,
getCaptcha,
pickFile,
} = useList(props)
return (
<ScrollView style={style.mRegisterWrap}>
<LinearProgress
style={[style.mLoading, isLoading ? style.mLoadingActive : {}]}
color="primary"
/>
<View style={style.mRegisterInner}>
<View style={style.mRegisterRow}>
<Input
label="邮箱"
style={style.mRegisterInput}
labelStyle={style.mRegisterLabelStyle}
value={username}
onChangeText={handleInput}
placeholder="邮箱"
// autoFocus
ref={usernameEl}
rightIcon={
<Icon
name={'help'}
onPress={toggleDialog1}
style={style.mInpntRightIcon}
></Icon>
}
></Input>
</View>
<View style={style.mRegisterRow}>
<Input
label="邮箱验证码"
style={style.mRegisterInput}
labelStyle={style.mRegisterLabelStyle}
value={emailCode}
onChangeText={setEmailCode}
placeholder="邮箱验证码"
rightIcon={
isSendEmail ? (
<Button
title={`${count}秒后重新发送`}
disabled
containerStyle={style.mSendEmailBtn}
/>
) : (
<Button
title="发送"
containerStyle={style.mSendEmailBtn}
onPress={handleSendEmail}
/>
)
}
></Input>
</View>
<View style={style.mRegisterRow}>
<View style={style.mRegisterAvavtarTextWrap}>
<Text style={style.mRegisterAvavtarText}>头像</Text>
</View>
{avatar ? (
<TouchableWithoutFeedback onPress={handleUploadAvatar}>
<Image
source={{ uri: avatar }}
style={style.mRegisterAvatar}
></Image>
</TouchableWithoutFeedback>
) : (
<View style={style.mRegisterUploadIcoWrap}>
<Icon
name={'add'}
onPress={handleUploadAvatar}
style={style.mRegisterUploadIcon}
></Icon>
</View>
)}
</View>
<View style={style.mRegisterRow}>
<Input
label="昵称"
style={style.mRegisterInput}
labelStyle={style.mRegisterLabelStyle}
value={nickname}
onChangeText={setNickname}
placeholder="昵称"
></Input>
</View>
<View style={style.mRegisterRow}>
<Input
label="密码"
style={style.mRegisterInput}
labelStyle={style.mRegisterLabelStyle}
value={password}
onChangeText={setPassword}
placeholder="密码"
secureTextEntry={!visible}
></Input>
<Icon
name={visible ? 'show' : 'close'}
onPress={handleVisilbe}
style={style.mRegisterPasswordIcon}
></Icon>
</View>
<View style={style.mRegisterRow}>
<Input
label="验证码"
style={style.mRegisterInput}
labelStyle={style.mRegisterLabelStyle}
value={code}
onChangeText={setCode}
placeholder="验证码"
></Input>
</View>
<View style={style.mRegisterCodeWrap}>
<TouchableWithoutFeedback onPress={getCaptcha}>
<WebView
originWhitelist={['*']}
source={{ html: captcha }}
></WebView>
</TouchableWithoutFeedback>
</View>
<View style={[style.mRegisterBtnWrap]}>
<Button onPress={handleLogin} type="solid" title="注册"></Button>
</View>
<View style={style.mRegisterMoreBtnWrap}>
<Button
containerStyle={style.mRegisterMoreBtn}
onPress={() => handleNav('Login')}
title="登录"
type="clear"
></Button>
<Button
containerStyle={style.mRegisterMoreBtn}
onPress={() => handleNav('IndexForTab')}
title="游客"
type="clear"
></Button>
</View>
<Divider width={1}></Divider>
<Button title="上传文件" onPress={pickFile} />
<Text style={style.mRegisterText}>注册</Text>
</View>
<Dialog isVisible={visible1} onBackdropPress={toggleDialog1}>
<Dialog.Title title="提示" />
<Text>某些用户的邮件会发送到垃圾箱</Text>
</Dialog>
</ScrollView>
)
}
useList.js:
import { useState, useRef, useEffect } from 'react'
import Api from '../../../api'
import Constants from 'expo-constants'
import Toast from 'react-native-root-toast'
import { checkEmail } from '../../../utils/tools'
import AsyncStorage from '@react-native-async-storage/async-storage'
import * as ImagePicker from 'expo-image-picker'
import * as DocumentPicker from 'expo-document-picker'
let timer
export default function useList(props) {
const [username, setUsername] = useState('')
const [emailCode, setEmailCode] = useState('')
const [isSendEmail, setIsSendEmail] = useState(false)
const [count, setCount] = useState(0)
const [emailId, setEmailId] = useState('')
const [avatar, setAvatar] = useState(null)
const [nickname, setNickname] = useState('')
const [password, setPassword] = useState('')
const [code, setCode] = useState('')
const [visible, setVisible] = useState(false)
// eslint-disable-next-line
const [isLoading, setIsLoading] = useState(false)
const [captchaId, setCaptchaId] = useState('')
const [captcha, setCaptcha] = useState('')
const [visible1, setVisible1] = useState(false)
const usernameEl = useRef(null)
const { navigation } = props
const toggleDialog1 = () => {
setVisible1(!visible1)
}
const handleInput = (e) => {
setUsername(e)
}
const handleSendEmail = () => {
const { isEmail, message } = checkEmail(username)
if (username.trim() === '') {
Toast.show('邮箱不能为空', {
duration: 3000,
position: Toast.positions.CENTER,
})
return
} else if (isEmail === false) {
Toast.show(message, {
duration: 3000,
position: Toast.positions.CENTER,
})
return
}
Api.h5.userSendEmailCode({ username }).then((res) => {
if (res.code === 200) {
setEmailId(res.data.emailId)
Toast.show(res.message, {
duration: 3000,
position: Toast.positions.CENTER,
})
setCount(Constants.manifest.extra.REACT_APP_MODE === 'dev' ? 6 : 180)
setIsSendEmail(true)
console.log('send11', res.data.emailId)
}
})
}
const handleLogin = () => {
const { isEmail, message } = checkEmail(username)
if (username.trim() === '') {
Toast.show('用户名不能为空', {
duration: 3000,
position: Toast.positions.CENTER,
})
return
} else if (isEmail === false) {
Toast.show(message, {
duration: 3000,
position: Toast.positions.CENTER,
})
return
} else if (password.trim() === '') {
Toast.show('密码不能为空', {
duration: 3000,
position: Toast.positions.CENTER,
})
return
} else if (code.trim() === '') {
Toast.show('验证码不能为空', {
duration: 3000,
position: Toast.positions.CENTER,
})
return
} else if (!emailId) {
Toast.show('请获取邮箱验证码', {
duration: 3000,
position: Toast.positions.CENTER,
})
return
}
Api.h5
.userAiAdd({
username,
emailCode,
emailId,
nickname,
password,
code,
captchaId,
})
.then((res) => {
if (res.code === 200) {
navigation.navigate('Login')
}
})
}
const handleNav = (path) => {
navigation.navigate(path)
}
const handleVisilbe = () => {
setVisible(!visible)
}
const handleUploadAvatar = async () => {
try {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
base64: true,
//allowsEditing: true,
//aspect: [4, 3],
//quality: 1,
})
if (!result.canceled) {
const formData = new FormData()
let uri = result.assets[0].uri
let uriArr = uri.split('/')
let name = uriArr[uriArr.length - 1]
console.log(uri)
setAvatar(uri)
formData.append('file', {
uri,
name,
//type: result.assets[0].type,
type: 'image/jpeg',
})
Api.h5
.uploadFile(formData)
.then((res) => {
console.log(res)
if (res.code === 200) {
console.log('成功')
Toast.show('上传成功', {
duration: 3000,
position: Toast.positions.CENTER,
})
}
})
.catch((err) => {
Toast.show('上传失败', {
duration: 3000,
position: Toast.positions.CENTER,
})
})
} else {
console.log('取消文件选择')
}
} catch (error) {
console.log('选择文件时出错', error)
}
}
const getCaptcha = () => {
console.log('captcha')
Api.h5.userCaptcha({}).then((res) => {
if (res.code === 200) {
const { captchaId, captcha } = res.data
let svg = captcha
let height = svg.indexOf('height')
let width = svg.indexOf('width')
let step1 = svg.slice(0, height + 8)
let step2 = svg.slice(height + 8 + 2)
svg = `${step1}150${step2}`
let step3 = svg.slice(0, width + 5)
let step4 = svg.slice(width + 8 + 3)
svg = `${step3}450${step4}`
let html = `<div style="text-align:center;width:100%;overflow:hidden;">${svg}</div>`
setCaptcha(html)
setCaptchaId(captchaId)
}
})
}
const pickFile = async () => {
try {
const result = await DocumentPicker.getDocumentAsync()
if (result.type === 'success') {
// 执行文件上传的逻辑
console.log(result.uri) // 上传文件的URI
console.log(result.name) // 上传文件的名称
console.log(result.size) // 上传文件的大小
console.log(result.mimeType) // 上传文件的类型
const formData = new FormData()
let uri = result.uri
let uriArr = uri.split('/')
let name = uriArr[uriArr.length - 1]
formData.append('file', {
uri: result.uri,
name: result.name,
type: result.mimeType,
})
Api.h5
.uploadFile(formData)
.then((res) => {
console.log(res)
if (res.code === 200) {
console.log('成功')
Toast.show('上传成功', {
duration: 3000,
position: Toast.positions.CENTER,
})
}
})
.catch((err) => {
Toast.show('上传失败', {
duration: 3000,
position: Toast.positions.CENTER,
})
})
} else {
console.log('取消文件选择')
}
} catch (error) {
console.log('选择文件时出错', error)
}
}
useEffect(() => {
;(async () => {
// 请求权限
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync()
if (status !== 'granted') {
console.log('拒绝访问图片库权限!')
} else {
console.log('已经获得访问图片库权限123!')
}
})()
}, [])
useEffect(() => {
getCaptcha()
}, [])
useEffect(() => {
clearTimeout(timer)
timer = setTimeout(() => {
if (count > 1) {
setCount(count - 1)
} else {
setIsSendEmail(false)
}
}, 1000)
// eslint-disable-next-line
}, [count])
useEffect(() => {
//usernameEl.current.focus()
//console.log(666, usernameEl.current.isFocused())
}, [])
useEffect(() => {
Api.h5.uploadGetTokenForH5().then(async (res) => {
if (res.code === 200) {
await AsyncStorage.setItem('qiniuUploadTokenForH5', res.data.token)
const qiniuUploadTokenForH5 = await AsyncStorage.getItem(
'qiniuUploadTokenForH5'
)
console.log('qiniuUploadTokenForH5', qiniuUploadTokenForH5)
}
})
}, [])
return {
username,
emailCode,
isSendEmail,
count,
avatar,
nickname,
password,
code,
visible,
isLoading,
captcha,
visible1,
usernameEl,
setEmailCode,
handleSendEmail,
setNickname,
setPassword,
setCode,
toggleDialog1,
handleInput,
handleLogin,
handleNav,
handleVisilbe,
handleUploadAvatar,
getCaptcha,
pickFile,
}
}
app.json:
{
"expo": {
"plugins": [
[
"expo-document-picker",
{
"iCloudContainerEnvironment": "Production"
}
]
]
}
}
参考链接:
https://docs.expo.dev/versions/latest/sdk/document-picker/
https://blog.csdn.net/xutongbao/article/details/131981469?spm=1001.2014.3001.5501