Closed rohankm closed 6 months ago
Thanks for reporting this! We're still trying to find a resource to replicate this problem and troubleshoot further. We'll report back when we have more info.
@stnguyen90 here is the manual code
it has
on ios it just creates a empty document with the file name on android it just throws network error
note : need to change the api url
import { Button, StyleSheet, Text, View } from "react-native";
import React from "react";
import { Client, Account, Storage } from "appwrite";
import axios from "axios";
const client = new Client();
client
.setEndpoint("https://demo.in/v1") // We set the endpoint, change this if your using another endpoint URL.
.setProject("62aa497432281eaabc7a"); // Your project ID
const account = new Account(client);
const storage = new Storage(client);
export default function App() {
const login = () => {
const promise = account.createEmailSession("demo@demo.com", "demodemo");
promise.then(
function (response) {
console.log(response); // Success
},
function (error) {
console.log(error); // Failure
}
);
};
const xhrupload = () => {
console.log("_--------------------------------------_");
const file = new File(["HELOOOO"], "finnw.txt", {
type: "text/plain",
});
let formData = new FormData();
formData.append("fileId", "unique()");
formData.append("file", file);
console.log("formData", formData);
const sendData = sendXmlHttpRequest(formData).then(
function (response) {
console.log("response", response); // Success
},
function (error) {
console.log("error", error); // Failure
}
);
};
const sdkupload = () => {
console.log("_--------------------------------------_");
const file = new File(["HELOOOO"], "fi.txt", {
type: "text/plain",
});
const promise2 = storage.createFile("userImages", "unique()", file);
promise2.then(
function (response) {
console.log(response); // Success
},
function (error) {
console.log(error); // Failure
}
);
return;
};
function sendXmlHttpRequest(data) {
const xhr = new XMLHttpRequest();
return new Promise((resolve, reject) => {
xhr.onreadystatechange = (e) => {
if (xhr.readyState !== 4) {
return;
}
console.log("xhr.status", xhr);
if (xhr.status === 201) {
resolve(JSON.parse(xhr.response));
} else {
reject("Request Failed");
}
};
xhr.open(
"POST",
"https://demo.in/v1/storage/buckets/userImages/files/"
);
xhr.withCredentials = true;
// xhr.setRequestHeader("content-type", "multipart/form-data");
xhr.setRequestHeader("X-Appwrite-Project", "62aa497432281eaabc7a");
xhr.setRequestHeader("X-Appwrite-Response-Format", "0.15.0");
xhr.setRequestHeader("x-sdk-version", "appwrite:web:9.0.1");
xhr.send(data);
});
}
const axiosUpload = () => {
console.log("_--------------------------------------_");
const file = new File(["HELOOOO"], "fi.txt", {
type: "text/plain",
});
let formData = new FormData();
formData.append("fileId", "unique()");
formData.append("file", file);
console.log("formData", formData);
axios({
url: "https://demo.in/v1/storage/buckets/userImages/files/",
method: "POST",
data: formData,
headers: {
"X-Appwrite-Project": "62aa497432281eaabc7a",
"X-Appwrite-Response-Format": "0.15.0",
"x-sdk-version": "appwrite:web:9.0.1",
},
})
.then(function (response) {
console.log("response :", response);
})
.catch(function (error) {
console.log("error from image :", error);
});
};
return (
<View style={styles.container}>
<Button onPress={login} title="login" />
<Button onPress={sdkupload} title="sdk upload data" />
<Button onPress={xhrupload} title="xhrupload upload data" />
<Button onPress={axiosUpload} title="axiosUpload" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
Finally I was able to solve the issue, thanks to @stnguyen90 for helping out
the issue was with the form data handled by the sdk.
here is my code and i have used custom form and XMLHttpRequest() to upload the images
import { Button, Image, StyleSheet, Text, View } from "react-native";
import React, { useState } from "react";
import * as ImagePicker from "expo-image-picker";
import { Client, Account } from "appwrite";
import axios from "axios";
const client = new Client();
const API_URL = "https://demo.in/v1";
const PROJECT_ID = "62aa497432281eaabc7a";
const BUCKET_ID = "userImages";
client
.setEndpoint(API_URL) // We set the endpoint, change this if your using another endpoint URL.
.setProject(PROJECT_ID); // Your project ID
const account = new Account(client);
export default function App() {
const [image, setImage] = useState(null);
const [succ, setSucc] = useState(false);
const login = () => {
const promise = account.createEmailSession("demo@demo.com", "demodemo");
promise.then(
function (response) {
console.log(response); // Success
},
function (error) {
console.log(error); // Failure
}
);
};
function sendXmlHttpRequest(data) {
const xhr = new XMLHttpRequest();
return new Promise((resolve, reject) => {
xhr.onreadystatechange = (e) => {
if (xhr.readyState !== 4) {
return;
}
console.log("xhr.status", xhr);
if (xhr.status === 201) {
resolve(JSON.parse(xhr.response));
} else {
reject("Request Failed");
}
};
xhr.open("POST", `${API_URL}/v1/storage/buckets/${BUCKET_ID}/files/`);
xhr.withCredentials = true;
// xhr.setRequestHeader("content-type", "multipart/form-data");
xhr.setRequestHeader("X-Appwrite-Project", PROJECT_ID);
xhr.setRequestHeader("X-Appwrite-Response-Format", "0.15.0");
xhr.setRequestHeader("x-sdk-version", "appwrite:web:9.0.1");
xhr.send(data);
});
}
const pickImage = async () => {
// No permissions request is necessary for launching the image library
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(result);
if (!result.cancelled) {
setImage(result.uri);
}
};
const uploadImage = async () => {
let filename = image.split("/").pop();
// Infer the type of the image
let match = /\.(\w+)$/.exec(image);
let type = match ? `image/${match[1]}` : `image`;
console.log("_--------------------------------------_");
let formData = new FormData();
formData.append("fileId", "unique()");
formData.append("file", {
uri: image,
name: filename,
type,
});
// formData.append("read", "");
// formData.append("write", "");
console.log("formData", formData);
await sendXmlHttpRequest(formData).then(
function (response) {
console.log("response", response); // Success
setSucc(true);
},
function (error) {
console.log("error", error); // Failure
}
);
};
return (
<View style={styles.container}>
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Button onPress={login} title="login" />
<Button title="Pick an image from camera roll" onPress={pickImage} />
</View>
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
{image && (
<>
<Image
source={{ uri: image }}
style={{ width: 200, height: 200 }}
/>
<Button onPress={uploadImage} title="uploadImage" />
</>
)}
{succ && <Text style={{ fontSize: 32 }}>UPLOADED</Text>}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
this is still an issue... the solution above works - for files less than the chunk size of 5mb... any thing larger than that doesn't work...
I've been looking into the module react-native-chunk-upload uploads chunks in the format *.tmp ... not sure if this is the reason why it doesn't work
It's still an issue with the SDK but this is a version of the code above using the fetch api instead of building the XMLHttpRequest and it's working good for my case :
fetch(`${API_URL}/v1/storage/buckets/${BUCKET_ID}/files/`, {
method: "POST",
headers: {
"content-type": "multipart/form-data",
"X-Appwrite-Project": PROJECT_ID,
"x-sdk-version": "appwrite:web:10.2.0",
},
body: formData,
credentials: "include",
});
I've been looking into the module react-native-chunk-upload uploads chunks in the format *.tmp ... not sure if this is the reason why it doesn't work
I tried it and also react-native-background-upload. When it comes to a chunked upload, i get a response code of 400, but only with React Native. The same code in a React Website works.
So, the crux of the problem with our SDK and react-native is FromData + File. In the browser, you can put a File into FormData and the browser will handle the multipart form request fine. React-Native, doesn't handle File the same way which is why you need to make formdata like:
let formData = new FormData();
formData.append("fileId", "unique()");
formData.append("file", {
uri: image,
name: filename,
type,
});
Fyi, this is what react native expects a file to be: https://github.com/facebook/react-native/blob/17ecae9ce7bded79ab3a083c9d07e15460e5635c/packages/react-native/types/modules/globals.d.ts#L107
Now, we'd like to use the same SDK and for both browser and react-native and we'd like the signature to kind of be the same. I'm not sure what makes sense from the react-native side, though. How else are people getting files besides ImagePicker
? And what do you have if not a URI?
So, the crux of the problem with our SDK and react-native is FromData + File. In the browser, you can put a File into FormData and the browser will handle the multipart form request fine. React-Native, doesn't handle File the same way which is why you need to make formdata like:
let formData = new FormData(); formData.append("fileId", "unique()"); formData.append("file", { uri: image, name: filename, type, });
Now, we'd like to use the same SDK and for both browser and react-native and we'd like the signature to kind of be the same. I'm not sure what makes sense from the react-native side, though. How else are people getting files besides
ImagePicker
? And what do you have if not a URI?
It's just file uri unless you want to convert it to a blob
btw here is my updated code and it works fine for me
const uploadToStorage = async (
bucketId,
uri,
permissions,
fileId = "unique()",
name,
ftype
) => {
// Infer the type of the image
const match = /\.(\w+)$/.exec(uri);
const filename = name ? `${name}.${match[1]}` : uri.split("/").pop();
const fileIdP = filename.split("_").pop().split(".").shift();
const type = ftype ? ftype : match ? `image/${match[1]}` : `image`;
const formData = new FormData();
formData.append("fileId", fileIdP);
formData.append("file", {
uri: uri,
name: filename,
type,
});
permissions.forEach((p) => {
formData.append("permissions[]", p);
});
const response = await fetch(
`${appwrite.config.endpoint}/storage/buckets/${bucketId}/files`,
{
method: "POST", // or 'PUT'
headers: {
...appwrite.headers,
"Content-Type": "multipart/form-data;",
},
body: formData,
}
);
return response.json();
};
hi , this doesn't work for the new web sdk version, any solution on how to upload images ?
I wish I could help but I migrated to supabase
@rohankm We now have React Native SDK. Please check it out at https://github.com/appwrite/sdk-for-react-native
๐ Reproduction steps
Im trying to upload files from react native expo
steps to reproduce
yarn create expo-app demo cd demo yarn add appwrite
here is my App.js
๐ Expected behavior
It should upload a file named filenamenew.txt to the specified bucket. The same works properly on react native web. but its not working in with android and ios
๐ Actual Behavior
Network request failed at node_modules\@babel\runtime\helpers\construct.js:19:9 in _construct at node_modules\@babel\runtime\helpers\wrapNativeSuper.js:26:22 in Wrapper at http://192.168.0.108:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&strict=false&minify=false:120669:293 in _createSuperInternal at node_modules\appwrite\dist\cjs\sdk.js:72:8 in AppwriteException#constructor at node_modules\appwrite\dist\cjs\sdk.js:391:22 in awaiter$argument_3 at node_modules\@babel\runtime\helpers\regeneratorRuntime.js:86:13 in tryCatch at node_modules\@babel\runtime\helpers\regeneratorRuntime.js:66:31 in
at node_modules\appwrite\dist\cjs\sdk.js:25:46 in rejected
at node_modules\promise\setimmediate\core.js:37:13 in tryCallOne
at node_modules\promise\setimmediate\core.js:123:24 in setImmediate$argument_0
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:248:12 in _allocateCallback$argument_0
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:112:14 in _callTimer
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:162:14 in _callReactNativeMicrotasksPass
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:413:41 in callReactNativeMicrotasks
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:391:6 in callReactNativeMicrotasks
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:133:6 in guard$argument_0
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:368:10 in guard
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:132:4 in flushedQueue
๐ฒ Appwrite version
Version 0.10.x
๐ป Operating system
Windows
๐งฑ Your Environment
No response
๐ Have you spent some time to check if this issue has been raised before?
๐ข Have you read the Code of Conduct?