如何使用 expo-image-picker 将文件从 iOS 上传到 Cloudinary?

Posted

技术标签:

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

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

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

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

【问题讨论】:

【参考方案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 上尝试过。

【讨论】:

以上是关于如何使用 expo-image-picker 将文件从 iOS 上传到 Cloudinary?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PHP 将文本区域数据从 HTML 文档发送到 MySQL 数据库?

如何将文本文档表示为特征向量进行文本分类?

如何将文本文档批量拆分为变量

GTK3下如何将文本域绑定到本地文件夹以获取文本

如何将文本行转换为有意义的单词[重复]

如何通过api将文本文档(ODT或DOCX)中的表格导出为EMF格式