这比我喜欢的要花更长的时间来解决,所以我在这里发布了解决方案。真正的关键是 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 上尝试过。