【问题标题】:How can I upload a file from iOS to Cloudinary using expo-image-picker?如何使用 expo-image-picker 将文件从 iOS 上传到 Cloudinary?
【发布时间】:2021-11-30 16:04:01
【问题描述】:

我正在使用expo/react-native,在 MacOS 上开发,在 iOS 模拟器上进行测试。按照各种示例,当我将从照片库中选择的文件发布到 Cloudinary 时,我不断收到 400(无效的 url,缺少 file 参数等)。不幸的是,Cloudinary 没有很好的 react-native 文档。

我还想显示进度指示器,因此不能使用fetch()

我直接从移动应用程序上传,而不是通过我的服务器,这就是 Cloudinary 的 nodeJS 文档不适用的原因。为了安全起见,这些上传请求最终会被签名。

【问题讨论】:

    标签: javascript react-native file-upload expo cloudinary


    【解决方案1】:

    这比我喜欢的要花更长的时间来解决,所以我在这里发布了解决方案。真正的关键是 Cloudinary 期望从移动设备以base64 的形式提供图像数据,而从 Web 提供本地文件 URL 就足够了。幸运的是,base64 数据可以直接从 expo-image-picker 组件获得,但它仍然必须格式化为 data uri 才能上传。

    import React, { useEffect, useState } from 'react';
    import * as ImagePicker from 'expo-image-picker';
    import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types';
    import { Platform, TouchableOpacity } from 'react-native';
    import { CloudinaryImage } from './CloudinaryImage';
    
    export const MyComponent = () => {
      const [cloudinaryId, setCloudinaryId] = useState('');
      const [progress, setProgress] = useState(100);
      useEffect(() => {
        (async () => {
          if (Platform.OS !== 'web') {
            const { status } =
              await ImagePicker.requestMediaLibraryPermissionsAsync();
            if (status !== 'granted')
              alert('Sorry, we need camera roll permissions to make this work!');
          }
        })();
      }, []);
    
      return (progress === 100) ?
        (
          <TouchableOpacity onPress={pickImage}>
            <CloudinaryImage cloudinaryId={cloudinaryId} />
          </TouchableOpacity>
        ) : ( ... a progress indicator of your choosing );
    

    pickImage 函数如下。这在用户触摸TouchableOpacity 组件时运行。

    const pickImage = async () => {
      const result = await ImagePicker.launchImageLibraryAsync({
        mediaTypes: ImagePicker.MediaTypeOptions.All,
        allowsEditing: true,
        aspect: [1, 1],
        quality: 1,
        base64: true, // this must be set for base64 to be returned!
      });
      if (!result.cancelled) {
        const { uri, base64 } = result;
        cloudinaryUpload({ image: { uri, base64 }, setProgress, setCloudinaryId });
      }
    };
    

    cloudinaryUpload 函数如下。这使用XMLHttpRequest 而不是fetch 以支持进度。

    const cloudName = 'yourCloud';
    const uploadPreset = 'yourPreset';
    const url = `https://api.cloudinary.com/v1_1/${cloudName}/upload`;
    
    export const cloudinaryUpload = async ({ image, setProgress, setCloudinaryId }) => {
      const xhr = new XMLHttpRequest();
      xhr.open('POST', url, true);
      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    
      // Update progress (can be used to show progress indicator)
      xhr.upload.addEventListener('progress', (e) => {
        const progress = Math.round((e.loaded * 100.0) / e.total);
        setProgress(progress);
      });
    
      // handle successful upload
      xhr.onreadystatechange = (e) => {
        if (xhr.readyState == 4 && xhr.status == 200) {
          const response = JSON.parse(xhr.responseText);
          setProgress(100);
          setCloudinaryId(response.public_id);
        }
      };
    
      xhr.addEventListener('error', (e) => console.error(e));
    
      const fd = new FormData();
      const data = `data:;base64,${image.base64}`; // the critical step!
      fd.append('file', data);
      fd.append('upload_preset', uploadPreset);
      fd.append('tags', 'your tags');
      setProgress(0);
      xhr.send(fd);
    };
    

    &lt;CloudinaryImage /&gt; 只是一个根据其 ID 呈现 Cloudinary 图像的组件。该组件的来源是另一个问题的答案。

    我还没有在 Android 上尝试过。

    【讨论】:

      猜你喜欢
      • 2022-12-03
      • 1970-01-01
      • 2020-09-16
      • 1970-01-01
      • 2016-11-28
      • 1970-01-01
      • 2021-12-02
      • 2020-10-26
      • 2021-09-28
      相关资源
      最近更新 更多