Open H3tansh opened 5 months ago
:warning: | Missing Reproducible Example |
---|---|
:information_source: | We could not detect a reproducible example in your issue report. Please provide either:
|
Hello,
I've come across your issue and would like to suggest a couple of potential solutions that might resolve the problem you're experiencing with FormData
on Android.
Firstly, ensure that the URI for the file you're appending to FormData
has the correct scheme. On Android, the file URI should start with file://
. Additionally, verify that the MIME type is accurate. For audio files, instead of 'audio/mp3', you should use 'audio/mpeg'.
Here's an updated snippet of your code with these changes:
const formData = new FormData();
formData.append('audio_file', {
name: fileName,
type: 'audio/mpeg', // Correct MIME type
uri: `file://${filePath}` // Correct URI scheme
});
try {
const response = await fetch('http endpoint', {
body: formData,
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer token'
}
});
console.log('test result ', response);
console.log('test result status ', response.status);
} catch (e) {
console.error('test error ', e);
}
accurate
Tried everything including your suggestion, still no luck 😓
I don't know if this helps OR is related, but we have been using FormData for 4+ years, and it broke when upgrading to 0.74.3 from 0.72, the culprit being
addition of
headers['content-disposition'] += `; filename="${
value.name
}"; filename*=utf-8''${encodeURI(value.name)}`;
specifically filename* part, so I've had to patch-package and revert it for time being.
I kept getting "request is malformed". YOu can debug network errors a bit better using Charles and SSL stripping, so you will see exactly what is being sent/received as well the format etc.
https://github.com/facebook/react-native/commit/7c7e9e6571c1f702213e9ffbb40921cd5a1a786b
@robertying
@chr4ss12 Not sure why you mentioned me. This change of filename*
added support for non-ascii filenames, and it's suggested to keep both filename=
and filename*=
in the header value for maximum compatibility. As the MDN doc says, "when both filename and filename are present in a single header field value, filename is preferred over filename when both are understood."
Check the encoded name. If the filename*=
part is correctly encoded, but you still get errors from the request, it might be an issue with the HTTP server that's handling the header.
@robertying I saw you created the commit (sorry if that's not the case - and you only approved it). I am using very simple code to upload files to google cloud storage, and it stopped working after that commit, there's nothing I can do with the HTTP server that's handling the header (the name= is always hardcoded to "myfile.jpg"),
just giving heads up to anyone else experiencing this issue.
@chr4ss12 I did make the change. This change was necessary to support utf-8 filenames.
If you could have a repro or try constructing the same header value in a form data request in Postman to isolate it, and see if it fixes the issue, it would be easier to see where it went wrong. I previously tested the multipart upload with Express.js and Multer, they received files fine.
@robertying I've checked the code, the spec, played around the request in Charles, and everything checks out - as far as I can tell it looks all right.
The problem is with google Cloud storage not knowing how to read the filename*=UTF-8''dummy.jpg part of the request - I will try and see if I can raise an issue in google tracker.
In meanwhile, am not too convinced of this change though, mainly because the way they implemented the syntax in the first place:
filename*=utf-8''
I. mean really? did they not want to throw in anything else that for sure will break ANY implementation that has no filename*= parsing support, unless that was their idea to begin with...
Also the app I use 'Charles' which I use for debugging could not parse the multipart data because it thought it is malformed (it is not), it seems this filename*= syntax is not that well adapted, perhaps something more along side of lines
if (name contains non ascii characters) //emit the filename*=
@chr4ss12 Yes in theory if the parser doesn't know the keys in the header value, it should throw them away, according to the doc. In practice though, I can definitely see implementations that don't conform to the spec.
Feel free to file a bug and propose a fix in a PR.
Thank you for investigating this. 🙏
We experienced this exact same issue due to this bug - it looks like it's either that we're not encoding the name correctly or the server cannot parse this value. In our case, our server is Go-based and will not parse the filename provided if there are any parentheses in the filename.
This is an example of a header that we're getting from React Native now, which is not being parsed correctly by Go.
form-data; name="1"; filename="84fb1493-321f-471f-9c63-7e98019b2931 (1).pdf"; filename*=utf-8''84fb1493-321f-471f-9c63-7e98019b2931 (1).pdf
This is the go stdlib for mime parsing: https://cs.opensource.google/go/go/+/refs/tags/go1.22.6:src/mime/mediatype.go;l=161-169
Do we know if encodeURI
is valid per the RFC to encode a filename in utf-8?
https://github.com/facebook/react-native/commit/7c7e9e6571c1f702213e9ffbb40921cd5a1a786b#diff-756cfe2421bc80e4c12e447a744cff5190da329b1a59614a7ebd853873f6a741R87
Should we not be using encodeURIComponent
instead?
Compared to encodeURI(), this function encodes more characters, including those that are part of the URI syntax.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
@Tadimsky encodeURIComponent
doesn't encode parentheses ()
either.
Maybe check if the server can parse the header if you get rid of the filename*=
part.
Ah you're right, sorry about that!
It looks like MDN has an example of how to create RFC-5987 valid values for Content-Disposition
which is a combination of encodeURIComponent
and some custom replacements.
I think we should probably be using something like that in React Native?
@Tadimsky Thanks so much for finding that. Would you like to raise a PR with that?
Yes, let me give that a try!
Hello,
I just encountered the same problem with the Content-Disposition, and came onto saw this issue. After some digging into the MDN docs, i found that this section states that the filename*
should not be there for a Content-Disposition
thats related to a form-data
.
Note that the request header does not have the filename* parameter and does not allow RFC 5987 encoding.
I hope I'm understanding that correctly. I can create a PR for this if necessary.
Hello,
I just encountered the same problem with the Content-Disposition, and came onto saw this issue. After some digging into the MDN docs, i found that this section states that the
filename*
should not be there for aContent-Disposition
thats related to aform-data
.Note that the request header does not have the filename* parameter and does not allow RFC 5987 encoding.
I hope I'm understanding that correctly. I can create a PR for this if necessary.
My understanding is exactly that as well.
This issue has caused unexpected issues for us too.
Hello, I Think i'm having the same problem here.
async postMultipart(url: string, data: any) {
const formData = new FormData();
let imageUrl: string = '';
Object.keys(data).map(key => {
console.log(key);
const value = data[key];
if (key == 'tags' || key == 'image') {
console.log('tags value will be ignored');
} else if (key == 'imageString') {
imageUrl = value;
} else if (key == 'aiGenerated') {
formData.append('ai_generated', value);
} else if (Array.isArray(value)) {
value.forEach(item => formData.append(`${key}`, item));
} else {
if (value === '') {
formData.append(key, 'none');
} else {
formData.append(key, value);
}
}
return true;
});
const imageBlob = await loadImageFile(imageUrl);
const extension = imageBlob.type.split('/')[1];
const fileName = imageUrl.split('/').pop();
console.log('value');
const file = await new File([imageBlob], fileName + '.' + extension);
console.log(file);
await formData.append('image', file, fileName + '.' + extension);
/*await formData.append('image', {
uri: imageUrl,
name: file.name,
type: file.type,
});*/
console.log('formData', formData);
const token = await getPersistData(DataPersistKeys.TOKEN);
const response = await fetch(this.config.url + url, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${token}`,
},
body: formData,
});
/*await this.axiosInstance.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
transformRequest: (data, headers) => {
return data;
},
}); */
return response;
}
Even when using the code that has been commented out, a network error keep occurs. (both fetch and axios)
Description
Cannot Make API call to http endpoint when using formData[Tried with both fetch and axios]
I've tried addring clearTextTrafic true in android Mainfest.xml file
Working fine in iOS, and working well with normal APIs ( without formData), only API with formData having this issue.
Steps to reproduce
const formData = new FormData() formData.append('audio_file', { name: fileName, type: 'audio/mp3', uri: filePath })
This returns network error
React Native Version
0.74.1
Affected Platforms
Runtime - Android
Output of
npx react-native info
Stacktrace or Logs
Reproducer
private repo cannot provide link
Screenshots and Videos
No response