ebay api error Uploaded picture has unsupported file format - flutter

I am trying to upload an image as a binary MIME attachment to ebays api UploadSiteHostedPictures. I keep receiving the following error: Uploaded picture has an unsupported file format. The image is a jpg but i have tried png, and that didn't work either.
I have gone through the ebay knowledgebase java and c# examples and i cannot understand where i am going wrong.
Here is where i am passing the image file.
List<int> imageBytes = imageFile.readAsBytesSync();
String base64Image = base64Encode(imageBytes);
Header
Map<String, String> uploadPictureHeader = {'X-EBAY-API-CALL-NAME': 'UploadSiteHostedPictures', 'X-EBAY-API-SITEID': '0', 'X-EBAY-API-RESPONSE-ENCODING': 'XML',
'X-EBAY-API-COMPATIBILITY-LEVEL': '967', 'X-EBAY-API-DETAIL-LEVEL': '0', 'Cache-Control': 'no-cache',
'Content-Type': 'multipart/form-data; boundary=FormBoundary7MA4YWxkTrZu0gW'};
Body
String requestUploadedSiteHostedPicture = '''--FormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="XML Payload"
<?xml version="1.0" encoding="utf-8"?>
<UploadSiteHostedPicturesRequest xmlns="urn:ebay:apis:eBLBaseComponents">
<RequesterCredentials>
<eBayAuthToken >$userTokenOAuth</eBayAuthToken>
</RequesterCredentials>
<PictureName>Vase</PictureName>
<PictureSet>Standard</PictureSet>
<ExtensionInDays>20</ExtensionInDays>
</UploadSiteHostedPicturesRequest>
--FormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="Vase"; filename="Vase.jpg"
Content-Transfer-Encoding: base64
$base64Image
--FormBoundary7MA4YWxkTrZu0gW--''';
Making the call
final response = await http.post(
url,
headers: uploadPictureHeader,
body: requestUploadedSiteHostedPicture,
encoding: Encoding.getByName("UTF-8")
);
I have used online binary converters to convert the image as well, but that didn't work either. Any help is much appreciated, thank you.

Related

Flutter send streamed multipart request not finishing and causing segfault

I'm trying to send a multipart request via http.StreamedRequest. I am aware of http.MultipartRequest, however, it does not fit my needs (the API I'm trying to send the request to encodes arrays by sending the same attribute multiple times, which is not possible using the fields parameter of http.MultipartRequest since it is a Map<String, String>, which does not allow the same key to be inserted multiple times).
The file (with form-data name document) I'm trying to send is provided in form of a Uint8List (a .pdf file), in addition to a filename (in the following "some_filename.pdf", a pdf title (in the following "some_document_title") and an array of numbers (which I'm trying to add to the multipart request) with the attribute tags.
For instance, I want the array tags=[1, 2, 3], title="some_document_title" and file to be encoded as follows:
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="title"
Content-type: text/plain
some_document_title
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="tags"
Content-type: text/plain
1
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="tags"
Content-type: text/plain
2
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="tags"
Content-type: text/plain
3
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="document"; filename="some_filename.pdf"
Content-type: application/octet-stream
After this part, I want to stream the file data.
The rough implementation looks like this:
Future<void> createDocument(Uint8List file, String filename, String title, List<int> tags) {
final StreamedRequest request = StreamedRequest('POST', Uri.parse('some_url'));
String boundary = "..."; // Generate random boundary string
String contentType = "application/octet-stream";
String body = "";
Map<String, String> fields = {
"title": title,
// Some more fields left out for readability
};
for (final fieldKey in fields.keys) {
body += _buildMultipartField(fieldKey, fields[fieldKey], boundary);
}
for (final tag in tags) {
body += _buildMultipartField('tags', tag.toString(), boundary);
}
// Prepare file part
body += "--$boundary" +
'\r\nContent-Disposition: form-data; name="document"; filename="$filename"' +
"\r\nContent-type: $contentType" +
"\r\n\r\n";
final closing = "\r\n--" + boundary + "--\r\n";
request.headers.addAll({
"Content-Type": "multipart/form-data; boundary=$boundary",
"Content-Length": "${body.length + closing.length + file.lengthInBytes}"
});
// Add current body
request.sink.add(utf8.encode(body));
Stream.fromIterable(file).listen((chunk) => request.sink.add([chunk]))
.onDone(() {
// Add closing to multipart request
request.sink.add(utf8.encode(closing));
request.sink.close();
});
final StreamdResponse response = await IOClient().send(request);
if (response.statusCode != 200) {
throw Error();
}
}
String _buildMultipartField(String fieldName, String value, String boundary) {
return '--$boundary' +
'\r\nContent-Disposition: form-data; name="$fieldName"' +
'\r\nContent-type: text/plain' +
'\r\n\r\n' +
value +
'\r\n';
}
Now, my current implementation produces the expected body, which is good, however, the streamed request does not seem to finish, and according to the DevTools Network page, the request is never sent.
Is the content-length relevant for the request to finish? I.e. is it possible that the request is not finishing because the server is still expecting data because the content-length is calculated incorrectly?
Also, I'm getting a segfault when hot restarting the application when the request is still pending (it never returns).
Any help is appreciated, thanks!

Mailparser ignores attachments' headers

I'm sending an email with attachments, which look like
const attachment = { filename, path, size, headers: { uid: 'someId' } };
According to the Nodemailer's docs I can set attachment's headers in the same format as message headers.
At the receiver's side the email is parsed by simpleParser (from mailparser). Parsed email looks great, it has all attachment's info but the headers are empty {}. But the raw email source has the following:
----_NmP-30615c8ac620489d-Part_1
Content-Type: image/jpeg; name=attachment.jpg
Uid: someId
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=attachment.jpg
The uid header is there, but it is lost after parsing.
Also tried headers in the following format: headers: [{ header: 'uid', value: 'someId' }]. But it doesn't help.
How can I get that headers correctly? Or this can be the mailparser's bug?
After few hours of mailparser's source code researching I discovered that mailparser returns headers as Map, that's why it looks like Object {} which seems to be empty, but it's not.

How to translate special characters in a string in a Flutter application? Example: "Déjà Vu" to "Déjà Vu"

I am new to Flutter development and I am trying to decode or translate special characters.
The example I am working with looks like this presented as normal text in flutter:
Example: "Déjà Vu" to "Déjà Vu"
The left is how it appears on the UI and the result I would like to see is on the right.
I have tried using Runes class via docs --> https://api.dart.dev/stable/1.24.3/dart-core/Runes-class.html but no luck.
This is the non-working code:
child: Text(new Runes("Déjà Vu").string)
Update:I tried to pass 'Content-type': 'application/json; charset=utf-8', in the API call, however it didn't seem to correct this particular issue. I will attach a snapshot of the response (I ran it with the new headers and also without)
Here is the code:
Future<http.Response> _attemptCall(String suffix) => http.get(
'$kBaseURL$suffix',
headers: {
'Authorization': 'Bearer $_accessToken',
'Content-type': 'application/json; charset=utf-8',
},
);
Future<T> _authorizedCall<T>(
String suffix,
T Function(String) decode,
) async {
if (_accessToken == '') {
await refreshToken();
}
http.Response response = await _attemptCall(suffix);
var resBody = response.body;
print('This is the response --> $resBody');
if (response.statusCode == 401) {
await refreshToken();
response = await _attemptCall(suffix);
}
if (response.statusCode == 200) {
return decode(response.body);
}
return null;
}
#override
Future<Episode> getEpisodeDetails(String id) => _authorizedCall(
_episodeDetailUrl(id),
(s) => Episode.fromJson(jsonDecode(s)),
);
This charset mangling is called Mojibake (Thanks Randal Schwartz for pointing out!)
You cannot change "Déjà Vu" back to "Déjà Vu", you have to take action on either the way data is encoded and sent or the way you decode the response.
See the binary representation of these utf-8 characters:
11000011 10000011 Ã --> there is no way to tell `Ã` that it should be `à`
11000010 10101001 ©
11000011 10100000 à
11000011 10101001 é
You need to fix the problem upstream with the either the API response:
Content-type: application/json; charset=utf-8
The API is sending you back a stream of bytes, and should be utf8 in order to avoid this kind of output.
Or the way you decode the stream of bytes, you may also change:
return decode(response.body)
To
return decode(utf8.decode(response.bodyBytes));

Sending int and boolean at body of http post method in flutter

Hi I have a http post as
final http.Response response = await client.post(
'http://someurl/',
headers: {
HttpHeaders.contentTypeHeader: "application/json",
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": token
},
body: {
"isItTake": false,
"servisID": 1
}
);
But when I try this post method I get "Unhandled Exception: type 'bool' is not a subtype of type 'String' in type cast". I can change the API to expect string but I wonder if there is a work around to send int or boolean.
Note that When I send a similar request on postman everything is fine.
Edit:
Postman:
POST /someendpoint/ HTTP/1.1
Host: somehost
Authorization: Token sometoken
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 20582fd0-c980-2d0d-fb2f-3bdd87d767f5 \
{
"isItTake": false,
"servisID": 1
}
Try sending the request body values as Strings and see if that works. I've faced this issue before with type mismatch with the request bodies of http requests and I'm not quite sure as to why it throws exceptions like that even though the documentation for the api clearly specifies the type for each value in the request body. Try this:
final http.Response response = await client.post(
'http://someurl/',
headers: {
HttpHeaders.contentTypeHeader: "application/json",
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": token
},
body: {
"isItTake": 'false',
"servisID": '1'
}
);
Or in case if you have your values in some bool and int variables:
final http.Response response = await client.post(
'http://someurl/',
headers: {
HttpHeaders.contentTypeHeader: "application/json",
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": token
},
body: {
"isItTake": isItTake.toString(),
"servisID": servisID.toString()
}
);
Use String encoded = json.encode(theMap); then post encoded. If you need a particular character encoding (e.g. utf-8) then further encode the string using utf8.encode(encoded) and post the resulting byte array. (The second step should be unnecessary for utf-8 as I think that is the default.)
It's worth considering what the 3 variants do:
List<int> - sends an opaque byte array
String encodes the string into bytes using a character encoding - and sends the byte array
Map<String, String> - encodes the string key/value pairs in
x-www-form-urlencoded and sends that.
If you want to send more complex data then you need to convert it into one of the above (and the server needs to know how to decode it). That's where the content-type header is useful. Ultimately, the server receives a byte array and converts it back into, for example, a string, or some json, or a set of form fields, or an image. It knows how to do this based on the header and any specified encoding.
Complete Credit: Source

File not uploading in server ionic3

I have a scenario where I am uploading a image file from my local drive (type jpeg or png) to an API endpoint using ionic. Here is my code below :
fileupload.html -->
//---------------------------------------------------
<ion-item>
<ion-input type="file" accept="image/*" (change)="changeListener($event)"> </ion-input>
</ion-item>
fileupload.ts -->
changeListener($event):void{
this.file=$event.target.files[0];
console.log(this.file);
console.log("upload...");
let regData={"file":this.file};
console.log("REGDATAA"+JSON.stringify(regData));
this.jira.postAttachment("PM-3",regData).subscribe(dataa=>{
console.log(dataa);
});
}
provider.ts -->
public postAttachment(key,data):Observable<any>{
console.log(JSON.stringify(data))
return this.http.post(this.api+'/issue/'+key+'/attachments',JSON.stringify(data),{
headers: new HttpHeaders()
.append('Authorization', `Basic ${this.auth.getAuthString()}`)
.append('Content-Type','multipart/form-data';boundary="-------xe3rtyhrfds")
.append("X-Atlassian-Token", "no-check")
.append("User-Agent", "xx")
});
}
every time I send the file it doesn't take the path and sends an empty response, here is the error below.
//----------------------------------------------------
[object File]: {lastModifiedDate: [date] Fri Sep 21 2018 17:42:46 GMT+0530 (India Standard Time), name: "download.jpg", size: 5056, type: "image/jpeg", webkitRelativePath: ""}
upload...
ion-dev.js (157,11)
REGDATAA{"file":{}}
ion-dev.js (157,11)
{"file":{}}
ion-dev.js (157,11)
ERROR [object Object]
I have resolved CORS issue and there is no problem with the same.
When I send the same response using postman it succeeds here is what I send in Postman.
Form-data
key - "file" (type file) value - "/path/to/my/file"
Headers
Content-type - application/json
x-attlassian token - no-check
Can somebody advice what is going wrong here.
Use FormData to upload file.
fileupload.ts
changeListener(event) {
const fd = new FormData();
this.file = event.target.files[0];
fd.append('file', this.file, this.file.name);
this.jira.postAttachment("PM-3",fd)
.subscribe(data => {
console.log(data);
});
}
provider.ts
postAttachment(key, fd): Observable<any> {
const httpOptions = {
headers: new HttpHeaders(
{ 'Content-Type': 'multipart/form-data' },
{ 'Authorization': `Basic ${this.auth.getAuthString()}` })
};
return this.http.post(this.api+'/issue/'+key+'/attachments', fd, httpOptions);
}
Your have to change the content type from application/json to multipart/form-data. You are sending an image, not a json-file.
Best way is to encode your image to base64 and send it. Everything depends about what your server needs.
or you can try this.
const body = file;
const headers = new Headers({'Content-Type': 'image/jpg'});
return this.http.post(this.api+'/issue/'+key+'/attachments, body, {headers: headers}). ...
For an issue on AngularJS what ended up working is (might a similar aproach help you too) :
make a hidden input de type file
set it's value in the changeListener function
make the file send from there afterwards
The reason being some built in propertie of the file input let's its value be recognised as File/Blob instead of the path many "complex" components use.
Also send it as multipart file as mentioned before.