如何使用 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);
;
<CloudinaryImage />
只是一个根据其 ID 呈现 Cloudinary 图像的组件。该组件的来源是另一个问题的答案。
我还没有在 android 上尝试过。
【讨论】:
以上是关于如何使用 expo-image-picker 将文件从 iOS 上传到 Cloudinary?的主要内容,如果未能解决你的问题,请参考以下文章