【问题标题】:How can I upload a photo with Expo?如何使用 Expo 上传照片?
【发布时间】:2017-03-01 01:34:00
【问题描述】:

我正在使用 Expo 制作一个应用程序,并希望让用户拍照或从他们的相机胶卷中挑选一张并将其上传到我的服务器。我该怎么做?

【问题讨论】:

    标签: react-native exponentjs expo


    【解决方案1】:

    使用 Expo ImagePicker API 显示相机或相机胶卷并获取有关所选图像的信息:

    async function takeAndUploadPhotoAsync() {
      // Display the camera to the user and wait for them to take a photo or to cancel
      // the action
      let result = await ImagePicker.launchCameraAsync({
        allowsEditing: true,
        aspect: [4, 3],
      });
    
      if (result.cancelled) {
        return;
      }
    
      // ImagePicker saves the taken photo to disk and returns a local URI to it
      let localUri = result.uri;
      let filename = localUri.split('/').pop();
    
      // Infer the type of the image
      let match = /\.(\w+)$/.exec(filename);
      let type = match ? `image/${match[1]}` : `image`;
    
      // Upload the image using the fetch and FormData APIs
      let formData = new FormData();
      // Assume "photo" is the name of the form field the server expects
      formData.append('photo', { uri: localUri, name: filename, type });
    
      return await fetch(YOUR_SERVER_URL, {
        method: 'POST',
        body: formData,
        headers: {
          'content-type': 'multipart/form-data',
        },
      });
    }
    

    有关包括服务器代码在内的更全面的示例,请参阅此 repo:https://github.com/exponent/image-upload-example

    【讨论】:

    • 如何一次上传多张图片?
    • 是的..如何一次上传所有照片。我的意思是使用 promise.all()
    • 谢谢兄弟,你让我开心。 . .
    • 肯定formData.append('photo2', { uri: localUri, name: filename, type }); 比多次请求更好?
    • 这对我不起作用,与邮递员获取生成的代码相比,您的 formData 仅使用两个参数,而邮递员使用 3。您能解释一下区别以及为什么邮递员可以这样做而这段代码可以吗?不为我做?
    【解决方案2】:

    官方示例使用 Node.js,下面是 PHP 示例:

    世博会

    async function takePhotoAndUpload() {
    
      let result = await ImagePicker.launchCameraAsync({
        allowsEditing: false, // higher res on iOS
        aspect: [4, 3],
      });
    
      if (result.cancelled) {
        return;
      }
    
      let localUri = result.uri;
      let filename = localUri.split('/').pop();
    
      let match = /\.(\w+)$/.exec(filename);
      let type = match ? `image/${match[1]}` : `image`;
    
      let formData = new FormData();
      formData.append('photo', { uri: localUri, name: filename, type });
    
      return await fetch('http://example.com/upload.php', {
        method: 'POST',
        body: formData,
        header: {
          'content-type': 'multipart/form-data',
        },
      });
    }
    

    上传.php

    <?php
        move_uploaded_file($_FILES['photo']['tmp_name'], './photos/' . $_FILES['photo']['name']);
    ?>
    

    【讨论】:

    • 我想将图片名称保存到数据库,将实际照片保存到服务器;我该怎么做?
    • 将两者都发布到服务器,将图像保存在硬盘上并将文件名保存在数据库中
    • 尽管我几乎在任何地方都能找到这个解决方案,但使用 Django Rest Framework 作为后端它对我不起作用。但是,传递 blob 的 base64 编码版本就像一个魅力。
    • 这在服务器端转储中不起作用 $_FILES 给出了这个:array(1) { ["photo"]=> array(5) { ["name"]=> string(40) "0125ff6e-b04e-4d76-b8d4-a3c411952f29.jpg" ["type"]=> string(0) "" ["tmp_name"]=> string(0) "" ["error"]=> int(1) [“大小”]=> int(0) } }
    【解决方案3】:
    import React, { Component } from 'react';
    import {
      ActivityIndicator,
      Button,
      Clipboard,
      Image,
      Share,
      StatusBar,
      StyleSheet,
      Text,
      TouchableOpacity,
      View,
    } from 'react-native';
    import { Constants } from 'expo';
    import * as Permissions from 'expo-permissions';
    import * as ImagePicker from 'expo-image-picker';
    export default class App extends Component {
      state = {
        image: null,
        uploading: false,
      };
    
      render() {
        let {
          image
        } = this.state;
    
        return (
          <View style={styles.container}>
            <StatusBar barStyle="default" />
    
            <Text
              style={styles.exampleText}>
              Example: Upload ImagePicker result
            </Text>
    
            <Button
              onPress={this._pickImage}
              title="Pick an image from camera roll"
            />
    
            <Button onPress={this._takePhoto} title="Take a photo" />
    
            {this._maybeRenderImage()}
            {this._maybeRenderUploadingOverlay()}
          </View>
        );
      }
    
      _maybeRenderUploadingOverlay = () => {
        if (this.state.uploading) {
          return (
            <View
              style={[StyleSheet.absoluteFill, styles.maybeRenderUploading]}>
              <ActivityIndicator color="#fff" size="large" />
            </View>
          );
        }
      };
    
      _maybeRenderImage = () => {
        let {
          image
        } = this.state;
    
        if (!image) {
          return;
        }
    
        return (
          <View
            style={styles.maybeRenderContainer}>
            <View
              style={styles.maybeRenderImageContainer}>
              <Image source={{ uri: image }} style={styles.maybeRenderImage} />
            </View>
    
            <Text
              onPress={this._copyToClipboard}
              onLongPress={this._share}
              style={styles.maybeRenderImageText}>
              {image}
            </Text>
          </View>
        );
      };
    
      _share = () => {
        Share.share({
          message: this.state.image,
          title: 'Check out this photo',
          url: this.state.image,
        });
      };
    
      _copyToClipboard = () => {
        Clipboard.setString(this.state.image);
        alert('Copied image URL to clipboard');
      };
    
      _takePhoto = async () => {
        const {
          status: cameraPerm
        } = await Permissions.askAsync(Permissions.CAMERA);
    
        const {
          status: cameraRollPerm
        } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
    
        // only if user allows permission to camera AND camera roll
        if (cameraPerm === 'granted' && cameraRollPerm === 'granted') {
          let pickerResult = await ImagePicker.launchCameraAsync({
            allowsEditing: true,
            aspect: [4, 3],
          });
    
          if (!pickerResult.cancelled) {
            this.setState({ image: pickerResult.uri });
          }
    
          this.uploadImageAsync(pickerResult.uri);
        }
      };
    
      _pickImage = async () => {
        const {
          status: cameraRollPerm
        } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
    
        // only if user allows permission to camera roll
        if (cameraRollPerm === 'granted') {
          let pickerResult = await ImagePicker.launchImageLibraryAsync({
            allowsEditing: true,
            base64: true,
            aspect: [4, 3],
          });
    
    
          if (!pickerResult.cancelled) {
            this.setState({ image: pickerResult.uri});
          }
    
          this.uploadImageAsync(pickerResult.uri);
        }
      };
    
     uploadImageAsync(pictureuri) {
      let apiUrl = 'http://123.123.123.123/ABC';
    
    
    
        var data = new FormData();  
        data.append('file', {  
          uri: pictureuri,
          name: 'file',
          type: 'image/jpg'
        })
    
        fetch(apiUrl, {  
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'multipart/form-data'
          },
          method: 'POST',
          body: data
        }).then(
          response => {
            console.log('succ ')
            console.log(response)
          }
          ).catch(err => {
          console.log('err ')
          console.log(err)
        } )
    
    
    
    
      }
    
    }
    
    const styles = StyleSheet.create({
      container: {
        alignItems: 'center',
        flex: 1,
        justifyContent: 'center',
      },
      exampleText: {
        fontSize: 20,
        marginBottom: 20,
        marginHorizontal: 15,
        textAlign: 'center',
      },
      maybeRenderUploading: {
        alignItems: 'center',
        backgroundColor: 'rgba(0,0,0,0.4)',
        justifyContent: 'center',
      },
      maybeRenderContainer: {
        borderRadius: 3,
        elevation: 2,
        marginTop: 30,
        shadowColor: 'rgba(0,0,0,1)',
        shadowOpacity: 0.2,
        shadowOffset: {
          height: 4,
          width: 4,
        },
        shadowRadius: 5,
        width: 250,
      },
      maybeRenderImageContainer: {
        borderTopLeftRadius: 3,
        borderTopRightRadius: 3,
        overflow: 'hidden',
      },
      maybeRenderImage: {
        height: 250,
        width: 250,
      },
      maybeRenderImageText: {
        paddingHorizontal: 10,
        paddingVertical: 10,
      }
    });
    

    【讨论】:

      【解决方案4】:

      由于选择的解决方案实际上并不适合我,因此我将使用 Expo 和 Django Rest Framework 作为后端进行文件上传。

          const blobToBase64 = blob => {
            const reader = new FileReader();
            reader.readAsDataURL(blob);
            return new Promise(resolve => {
              reader.onloadend = () => {
                resolve(reader.result);
              };
            });
          };
      
          const formData = new FormData();
          const base64 = await blobToBase64(blob);
          formData.append('file', base64);
          formData.append('data', JSON.stringify(payload));  // additional data (I parse the string as json in the backend to get my payload back)
      
          // same code as chosen answer, this was not part of the problem
          return await fetch(YOUR_SERVER_URL, {
            method: 'POST',
            body: formData,
            headers: {
              'content-type': 'multipart/form-data',
            },
          });
      

      在 Django 中,我可以使用自定义解析器将 base64 字符串解码为字节,然后用它创建一个 SimpleUploadedFile 对象。

          class MultipartJsonParser(parsers.MultiPartParser):
            def parse(self, stream, media_type=None, parser_context=None):
              result = super().parse(
                  stream,
                  media_type=media_type,
                  parser_context=parser_context
              )
      
              base64_file = result.data.get('file')
              file_parts = base64_file.split(',')
              mime_type = re.sub(r'^data:([\w\/]+);base64$', '\\1', file_parts[0])
              file = SimpleUploadedFile('file', base64.b64decode(file_parts[1]), mime_type)
              data = json.loads(result.data["data"]) or {}  # additional data sent by Expo app
              qdict = QueryDict('', mutable=True)
              qdict.update(data)
              return parsers.DataAndFiles(qdict, {'file': file})
      
          class MyUploadView(ModelViewSet):
            parser_classes = (MultipartJsonParser, parsers.JSONParser)
      
            def create(self, request, *args, **kwargs):
                # request.data should have a 'file' property with a SimpleUploadedFile object
                ...
      
      

      【讨论】:

      • 为了让 Rails 5.2 工作,必须删除内容类型的标题
      【解决方案5】:

      对于 Expo,除了使用 Expo FileSystem uploadAsync 之外,没有什么对我有用

      uploadImage = async ({ imageUri } }) => FileSystem.uploadAsync(
          apiUrl,
          imageUri,
          {
            headers: {
              // Auth etc
            },
            uploadType: FileSystem.FileSystemUploadType.MULTIPART,
            fieldName: 'files',
            mimeType: 'image/png',
          });
      

      注意 - imageUri 格式为 file:///mypath/to/image.png

      【讨论】:

        【解决方案6】:

        对于没有找到或解决此问题的人。我花了三天时间寻找解决方案,我得到了它。在我的情况下,它是数据对象的命名元素。 Android 版本 10,世博会 4.11.0,

        这是前面

        async function uploadImage(uploadFile){
            const data = new FormData()
            data.append('name',{
                name: image_name, {/* name your image whatever you want*/}
                type: 'image/jpeg', {/* type of image that you're uploading*/}
                uri: uploadFile {/*data, file or image from ImagePicker here you should pass uri data but not all data from ImagePicker*/}
            })
        {/*names of data object should be like this: name, type, uri*/}
            const response = await fetch(my_upload_api.php,{
                method: 'POST',
                body: data,
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            })
        }
        

        这是 PHP 的后端

        if(!empty($_FILES['name']['name'])){
        $target_dir = 'my folder where I put all images';
        if(!file_exists($target_dir)){
            $data = array(
                (object)array(
                    'code' => '400',
                    'message' => 'Can\'t fined folder.'
                )
            );
            $json = json_encode($data);
            echo $json;
            die();
        }
        $target_file = $target_dir . basename($_FILES['name']['name']);
        $image_file_type = pathinfo($target_file,PATHINFO_EXTENSION);
        if(file_exists($target_file)){
            $data = array(
                (object)array(
                    'code' => '400',
                    'message' => 'Sorry. File already exists.'
                )
            );
            $json = json_encode($data);
            echo $json;
            die();
        }
        if($_FILES['name']['size'] > 50000000){
            $data = array(
                (object)array(
                    'code' => '400',
                    'message' => 'Sorry. Your file is too large.'
                )
            );
            $json = json_encode($data);
            echo $json;
            die();
        }
        if(move_uploaded_file($_FILES['name']['tmp_name'], $target_file)){
            $data = array(
                (object)array(
                    'code' => '200',
                    'message' => 'Successfuly your file has been uploaded.',
                    'name' => $_FILES['name']
                )
            );
            $json = json_encode($data);
            echo $json;
            die();
        }else{
            $data = array(
                (object)array(
                    'code' => '400',
                    'message' => 'Sorry. There was something wrong. Try it again.'
                )
            );
            $json = json_encode($data);
            echo $json;
            die();
        }
        

        }

        这是我第一次尝试寻找解决方案的博客。如果解决方案在这里,我可能会花一天或更少的时间来解决这个问题。我希望我能帮助别人。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-14
          • 1970-01-01
          • 2014-12-10
          • 2010-11-13
          • 1970-01-01
          相关资源
          最近更新 更多