上一篇說了文件選擇的使用,java
https://my.oschina.net/HhhXxxJjj/blog/1936008react
選了文件就要上傳,這篇就記錄一下文件上傳web
這是antd的一個UI庫,不知道的同窗自行去學習,選擇圖片和拍照有時間在補上,他們的上傳方法是同一個,還包含了一點點RN和webview的交互以及組件封裝調用json
https://rn.mobile.ant.design/components/toast-cn/redux
一、調用各自文件選擇方式react-native
延遲調用文件選擇是爲了用戶體驗,以及權限獲取延時問題數組
二、上傳前的一些處理antd
咱們不能讓用戶傳一些亂七八糟的東西上去,因此要對文件進行篩選,咱們就簡陋的用文件後綴名篩選一下就好了app
四、調用上傳文件的方法dom
this.props.sendWebMessage(fileList)這是上傳成功後和WebView交互,有時間會記一下RN和WebView的交互
五、這是上傳方法
其中必須的的參數,參數名不能改,若是上傳的文件是中文名,還要對name字段編碼(後臺接收須要解碼,才能獲得真正的文件名),否則會報相似的錯誤
unexpected char 0*6587 at 34 in content-disposition
引用組件的代碼片斷
/** * @Date: 2018-08-08T17:25:36+08:00 * @Email: xiao.hxj@qq.com * @Last modified time: 2018-08-23T08:17:26+08:00 */ import React, { Component } from 'react'; import { StyleSheet, View, Text, WebView, AsyncStorage} from 'react-native'; import { deviceWidth ,isIOS,deviceHeight } from '../../../utils/common'; import { _Download } from '../../../utils/downloadImage'; import { connect } from 'react-redux'; import {ActionSheet, Toast} from "antd-mobile-rn"; import Header from "../../../components/Common/Header"; import Modal from "../../../components/Common/Modal"; import NoDate from "../../../components/Common/NoDate"; import NoMore from "../../../components/Common/NoMore"; import LoadingMore from "../../../components/Common/LoadingMore"; const isIPhone = new RegExp('\\biPhone\\b|\\biPod\\b', 'i').test(window.navigator.userAgent); let wrapProps; if (isIPhone) { wrapProps = { onTouchStart: e => e.preventDefault(), }; } class Outweb extends Component { constructor(props){ super(props); this.state = { visible:false, // url: "http://..../appjump" // url: "http://..../appjump" url: "http://..../appjump" } } async componentDidMount(){ let token = await AsyncStorage.getItem('token') let url = `${this.state.url}?token=${token}` this.setState({url:url.replace(/"/g,'')}) console.log(url.replace(/"/g,'')); } // 開始加載 onLoadStart(e){ Toast.loading('加載中...', 0) } // 加載成功 onLoad (){ Toast.hide() } // 加載失敗 onError(e){ Toast.hide() this.props.navigation.goBack() Toast.info('應用加載失敗,請稍後重試',2) } // 上傳組件 getMessage onMessage(e){ const message = e.nativeEvent.data if(message === 'upload'){ Toast.loading('加載中...',0) this.setState({visible:true}) }else { // console.log('*************',message); _Download(message); } } // 回調 sendWebMessage(fileList){ this.refs.webview.postMessage(fileList); } render(){ return( <View style={styles.page}> <Header title={"外部頁面"} goBack={() => {this.props.navigation.goBack();Toast.hide()}}></Header> <WebView ref={'webview'} onLoadStart = {this.onLoadStart.bind(this)} onLoad = {this.onLoad.bind(this)} onError = {this.onError .bind(this)} onMessage = {this.onMessage.bind(this)} automaticallyAdjustContentInsets={false} startInLoadingState scalesPageToFit = {true} javaScriptEnabled = {true} domStorageEnabled = {true} style={styles.webView} source={{uri: this.state.url,method: 'GET'}} /> <Modal sendWebMessage={this.sendWebMessage.bind(this)} visible={this.state.visible} /> </View> ) } } const styles = StyleSheet.create({ page:{ flex:1, backgroundColor:"#fff", }, webView: { height: isIOS ? (deviceHeight-44):(deviceHeight-64), width: deviceWidth }, }) export default connect(({cstmrModel, loading}) => ({ cstmrModel, loading:loading.models.cstmrModel }))(Outweb)
組件代碼
/** * @Date: 2018-08-21T15:52:02+08:00 * @Email: xiao.hxj@qq.com * @Last modified time: 2018-08-23T09:51:26+08:00 */ import React from "react"; import { connect } from 'react-redux' import { View, Text, StyleSheet, Image, TouchableOpacity } from "react-native"; import { activeOpacity ,acceptFile } from "../../utils/common"; import { ActionSheet, Toast, Modal} from "antd-mobile-rn"; import ImagePicker from 'react-native-image-crop-picker'; import RNFileSelector from 'react-native-file-selector'; import { FileUpload } from '../../utils/FileUpload' import RNFS from 'react-native-fs' const isIPhone = new RegExp('\\biPhone\\b|\\biPod\\b', 'i').test(window.navigator.userAgent); let wrapProps; if (isIPhone) { wrapProps = { onTouchStart: e => e.preventDefault(), }; } class UploadModal extends React.PureComponent { constructor(props) { super(props); this.state={ visible:false } } componentWillUnmount(){ ImagePicker.clean().then(() => { console.log('clean file cache'); }).catch(e => { console.log(e); }); } async fileUpload(fileAry) { Toast.loading('上傳中...',0) try { const res = await FileUpload(fileAry) if (res.success) { let fileList = JSON.stringify(res.attList) this.props.sendWebMessage(fileList) Toast.hide() }else { Toast.hide() Toast.info(res.message,2) } } catch (e) { Toast.hide() // console.log(e); } } // file上傳 upLoadFile(){ let that = this; Toast.hide() RNFileSelector.Show( { title: '選擇文件', closeMenu: true, onDone: (path) => { let params = [{ mime:'', path:`file://${path}` }] let fileArr = path.split('.'); if (fileArr.length > 1 && acceptFile.indexOf(fileArr[fileArr.length-1]) !==-1) { params.mime = `.${fileArr[fileArr.length-1]}` // console.log('params*****',params); that.fileUpload(params) }else { Toast.info('文件類型錯誤,請從新選擇!',2); } }, onCancel: () => { console.log('cancelled') } }) } // image上傳 upLoadImage(params){ let that = this; Toast.hide() if (params==='camera') { ImagePicker.openCamera({ width: 300, height: 400, multiple: true, hideBottomControls:true, }).then(image => { this.fileUpload(image) }).catch((e)=>{ Toast.info('未選擇文件或文件異常!',2) }); }else { ImagePicker.openPicker({ mediaType:'photo', multiple: true }).then(images => { that.fileUpload(images) }).catch((e)=>{ Toast.info('未選擇文件或文件異常!',2) }); } } showActionSheet(){ Toast.hide(); let that = this; const BUTTONS = [ '拍攝', '選擇圖片', '選擇文件', '取消', ]; ActionSheet.showActionSheetWithOptions( { maskClosable:false, options: BUTTONS, cancelButtonIndex: 3, }, (buttonIndex: any) => { if (BUTTONS[buttonIndex]==='拍攝') { Toast.loading('加載中...',0) this.timer = setTimeout(()=>{that.upLoadImage('camera')},200) }else if (BUTTONS[buttonIndex]==='選擇圖片') { Toast.loading('加載中...',0) that.timer = setTimeout(()=>{that.upLoadImage()},200) } else if (BUTTONS[buttonIndex]==='選擇文件') { Toast.loading('加載中...',0) that.timer = setTimeout(()=>{that.upLoadFile()},200) } }, ); } render() { const { visible } = this.props return ( <View> { visible ? this.showActionSheet():null } </View> ); } } const styles = StyleSheet.create({ content:{ flexDirection:'row', justifyContent:'space-around', alignItems:'center' }, img:{ height:60, width:60, } }); export default connect(({ M_My }) => ({ M_My }))(UploadModal);
上傳組件代碼
import { AsyncStorage } from 'react-native'; import RequestUrl from "../services/RequestUrl"; import { Storage } from "../utils"; const baseUrl = async () => { ...... } export const FileUpload = async(fileAry) => { let formData = new FormData(); let token = await AsyncStorage.getItem('token'); let serverUrl = await baseUrl(); if (!fileAry.length) return {success:false,message:'未選擇文件'}; //由於須要上傳多個文件,因此須要遍歷數組,把文件的路徑數組放入formData中 for( let i = 0; i < fileAry.length; i++ ){ let path = fileAry[i].path; let arr = path.split('/');//截取獲取文件名 // 文件的類型,以及中文文件名編碼 let file = { uri: path, type: 'multipart/form-data', name: escape(arr[arr.length-1]), fileType: fileAry[i].mime }; //這裏的key(uri和type和name)不能改變, formData.append("file", file); //這裏的files就是後臺須要的key //這裏的files就是後臺須要的key } // console.log(formData); let response = await fetch(`${serverUrl}${RequestUrl.FILE_UPLOAD}`,{ method:'POST', headers:{ // 'Accept': 'Application/json', 'Content-Type':'multipart/form-data', 'token': token, 'moduelType':8 }, body:formData, }) // console.log('response',await response.json()); return await response.json(); }