Content-Type header is text/html when uploading a file to the server using a multipart request only in iOS - flutter

I'm sending a multipart request to a server in order to upload a file, however this is working only on Android, when it comes to iOS I get an Internal Server Error with a status code 500. When I print the headers on Android the content type is application json, but on iOS the content type is text/html. Is this a server-side problem? or should I set any other header in the multipart request besides authorization header?
This is the code of the request
Future<Map<String, dynamic>> uploadFileToEvent(File file, int eventId) async {
var stream = http.ByteStream(file.openRead());
stream.cast();
var length = await file.length();
String url = '$_url/event/$eventId/media/';
final _header = <String, String>{
'Authorization': 'Bearer ' + _prefs.token,
};
var uri = Uri.parse(url);
var request = http.MultipartRequest("POST", uri);
var multipartFileSign = http.MultipartFile('file', stream, length, filename: basename(file.path));
request.files.add(multipartFileSign);
request.headers.addAll(_header);
var response = await request.send();
final resp = await http.Response.fromStream(response);
}

Related

Fail to get http response with form-data

I am not able to get response from http POST in Flutter but URL and data are verified in Postman.
var map = new Map<String, String>();
final url = Uri.parse(globals.ServerDomain + '/login');
Map<String, String> requestBody = <String, String>{
'username': '80889099',
'password': '123456789abcde'
};
var request = http.MultipartRequest('POST', url)
..fields.addAll(requestBody);
var response = await request.send();
final respStr = await response.stream.bytesToString();
print(respStr);
no result returned.
Flutter has this great package called Dio to handle all sort of http requests. It's very easy to do what you want with it, you are using form data so this is what you should use. For more details check this https://pub.dev/packages/dio#sending-formdata
Example code:
final formData = FormData.fromMap(
{'username': '80889099',
'password': "123456789abcde",
});
final response = await dio.post('${globals.ServerDomain}/login', data: formData);
Looks like Its big project changing the package is not a good option try adding request header
'Content-Type': 'multipart/form-data'
example code :
Map<String, String> headers= <String,String>{
'Authorization':'Basic ${base64Encode(utf8.encode('user:password'))}',//your any other header
'Content-Type': 'multipart/form-data'
};
var map = new Map<String, String>();
final url = Uri.parse(globals.ServerDomain + '/login');
Map<String, String> requestBody = <String, String>{
'username': '80889099',
'password': '123456789abcde'
};
var request = http.MultipartRequest('POST', URL)
..headers.addAll(headers)
..fields.addAll(requestBody);
var response = await request.send();
final respStr = await response.stream.bytesToString();
print(respStr);
Additionally check if internet permission is given (may not be the cause just info)

Unable to Upload Image but Server response indicates success

Each time I try to upload an image using http multipart, I get success response in less than a second, which is weird and indicates that the image wasn't uploaded. How do I get over this issue please:
Future<UpdatedUserDataModel> updateUserData(
{String accessToken, String field, String value}) async {
String params = '?access_token=$accessToken';
String url = AppStrings.apiBase + AppStrings.updateUserData + params;
Map<String, String> headers = {"Content-type": "multipart/form-data"};
//Prepare Multipart request
http.MultipartRequest request = http.MultipartRequest(
'POST',
Uri.parse(url),
);
request.fields['api_key'] = AppStrings.apiKey;
request.headers.addAll(headers);
request.files.add(
await http.MultipartFile.fromPath(
field,
value,
filename: 'cover_image',
contentType: MediaType('image', value.split('.').last),
),
);
//Send request
final response = await request.send();
//Decode response stream here...
}

Flutter request Filemaker API (FieldData)

Im trying to access one specific call to the filemaker API, I have several requests which are working..
but if i try to do one with the fieldData field it doesn't work
var body = {"fieldData": {
"testId": myId
}};
HttpClient httpClient = new HttpClient();
HttpClientRequest request = await httpClient.postUrl(Uri.parse(url));
request.headers.set('content-type', 'application/json');
request.headers.add('authorization', 'bearer $token');
request.add(utf8.encode(json.encode(body)));
HttpClientResponse httpClientResponse = await request.close();
String reply = await httpClientResponse.transform(utf8.decoder).join();
httpClient.close();
I just get the error:
{"messages":[{"message":"Unknown parameter(s): fieldData","code":"960"}],"response":{}}
Edit:
Url: https://{Server}/fmi/data/vLatest/databases/{database}/layouts/DataAPIaddresses/records/{id}
Found it:
HttpClientRequest request = await httpClient.patchUrl(Uri.parse(url));
You have to patchUrl, because Filemaker wants a Patch request...

How to send a png file by 'application/octet-stream' in Dart/Flutter to Microsoft Custom Vision?

I know this question could be redundant, but I am trying to send a png file through a POST request to Microsoft Custom Vision in Flutter.
This is my code:
void _makeRequest (File file) async {
String url = "<url>";
Map<String, String> headers = {
"Content-Type": "application/octet-stream",
"Prediction-Key": "<key>",
};
var bytes = file.readAsBytesSync();
var response = await http.post(
url,
headers: headers,
body: bytes,
);
print(response.body);
print(response.statusCode);
}
And when I run this code I get this response:
{"code":"ErrorUnknown","message":"The request entity's media type 'appliction/octet-stream' is not supported for this resource."}
Based on your comment , I think you used the wrong endpoint/URL. Since you're sending image, you have to use the other prediction endpoint that looks like this:
"https://southcentralus.api.cognitive.microsoft.com/customvision/v3.0/Prediction/<Project ID>/classify/iterations/<Iteration number>/image
^ Note ../image
If still can't, please try below code snipper(works for me):
final bytes = file.readAsBytesSync();
var uri = Uri.parse(
"<Prediction endpoint>");
var request = new http.Request("POST", uri)
..headers['Prediction-Key'] = "<Prediction Key>"
..headers['Content-Type'] = "application/octet-stream"
..bodyBytes = bytes;
http.Response response = await http.Response.fromStream(await request.send());
print(request);
print("Result: ${response.statusCode}");
print(response.statusCode);
print(response.body);

Flutter Resumable Upload to Google Drive Through HTTP

Based off the documentation on Google Drive API I'm trying to upload a file to the root folder of a Google Drive. I have authentication of the user through Google Sign In, and that hasn't been an issue. I keep getting a 411 Error returned from the server that says
"POST requests require a Content-length header. That’s all we know.".
I have a Content-length header but it seems to not be accepted. Here's the code I have,
Uri uri = Uri.parse('https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable');
http.MultipartRequest request = new http.MultipartRequest('POST', uri);
request.headers["Authorization"] = header['Authorization'];
request.headers['content-type'] = "application/json; charset=UTF-8";
request.headers['X-Upload-Content-Type'] ='video/mp4';
request.headers['X-Upload-Content-Length'] = lengthInBytes.toString();
request.headers['name'] = fileName;
request.headers['content-length'] = (request.contentLength).toString();
//request.files.add(await http.MultipartFile.fromPath('$fileName', file.path,));
print("request.toString: " + request.toString());
http.StreamedResponse response = await request.send();
print('ok: ' + response.statusCode.toString());
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
The only line that I know looks off to me is the fileName, as the documentation on the API site is slightly different and I'm not sure if I'm encoding it correctly. Here's the API example on the Google site,
POST https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable HTTP/1.1
Authorization: Bearer [YOUR_AUTH_TOKEN]
Content-Length: 38
Content-Type: application/json; charset=UTF-8
X-Upload-Content-Type: image/jpeg
X-Upload-Content-Length: 2000000
{
"name": "myObject"
}
I can do a multipart upload for a file about 5MB in size, but I need to be able to upload larger ones and resumable is the only option. I can post the multipart code that works if needed.
I solved the issue by using the http StreamedRequest class. The code posted below works with Google Drive V3 to upload a mp4 video.
Future handleUploadData(Map headers, String filename, String path) async {
final file = new File(path);
final fileLength = file.lengthSync().toString();
String sessionUri;
Uri uri = Uri.parse('https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable');
String body = json.encode({ 'name' : filename });
final initialStreamedRequest =
new http.StreamedRequest('POST', uri)
..headers.addAll({
'Authorization': headers['Authorization'],
'Content-Length' : utf8.encode(body).length.toString(),
'Content-Type' : 'application/json; charset=UTF-8',
'X-Upload-Content-Type' : 'video/mp4',
'X-Upload-Content-Length' : fileLength
});
initialStreamedRequest.sink.add(utf8.encode(body));
initialStreamedRequest.sink.close();
http.StreamedResponse response = await initialStreamedRequest.send();
print("response: " + response.statusCode.toString());
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
if (response.statusCode == 200) {
sessionUri = response.headers['location'];
print(sessionUri);
}
Uri sessionURI = Uri.parse(sessionUri);
final fileStreamedRequest =
new http.StreamedRequest('PUT', sessionURI)
..headers.addAll({
'Content-Length' : fileLength,
'Content-Type' : 'video/mp4',
});
fileStreamedRequest.sink.add(file.readAsBytesSync());
fileStreamedRequest.sink.close();
http.StreamedResponse fileResponse = await fileStreamedRequest.send();
print("file response: " + fileResponse.statusCode.toString());
fileResponse.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}
The initial StreamRequest sends a request to GDrive with meta data about the file that will be uploaded, and receives a location URI that is used in the second file StreamRequest to upload the actual file data. Currently this is done in one upload action, but it could be split up into chunks.
I had roughly the same problem except I was trying to upload a text file and I wanted a single atomic request in order to be able to use the "If-Match" header with the file etag (When I'll write "update" code, I'm doing sync and I don't want to overwrite the file if it was changed by somewhere else during my sync).
I was really struggling with the http.post function and I was getting the "411 length required" error even though I was properly setting the "Content-Length" header.
The solution from Sean Coutinho using http.StreamedRequest gave me working code I could work from to get my request working, so thank you!
I'll post my code here in case it helps other people:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:google_sign_in/google_sign_in.dart';
enum RemoteFileType {
FOLDER,
FILE,
}
class RemoteFile {
final RemoteFileType fileType;
final String fileId;
final String fileName;
RemoteFile(
this.fileType,
this.fileId,
this.fileName,
);
}
// The boundary string
const String MULTIPART_REQUESTS_BOUNDARY_STRING = 'foo_bar_baz';
Map<String, String> _authHeaders;
String _createMultiPartRequestBodyString(
final Map<String, dynamic> requestMetaData,
final String fileContentString,
) {
return '\r\n--$MULTIPART_REQUESTS_BOUNDARY_STRING\r\n' +
'Content-Type: application/json; charset=UTF-8\r\n\r\n' +
jsonEncode(requestMetaData) +
'\r\n--$MULTIPART_REQUESTS_BOUNDARY_STRING\r\nContent-Type: text/plain\r\n\r\n' +
fileContentString +
'\r\n--$MULTIPART_REQUESTS_BOUNDARY_STRING--';
}
Future<RemoteFile> createNewTextFile(
final RemoteFile parentFolder,
final String fileName,
final String fileTextContent,
) async {
final Map<String, dynamic> requestMetaData = {
'mimeType': 'application/json',
'title': fileName,
'parents': [
{'id': parentFolder.fileId}
],
};
final String multiPartRequestBodyString = _createMultiPartRequestBodyString(requestMetaData, fileTextContent);
final http.StreamedRequest fileStreamedRequest = http.StreamedRequest(
'POST',
Uri.parse('https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart'),
);
fileStreamedRequest.headers.addAll({
'Authorization': _authHeaders['Authorization'],
'Accept': 'application/json',
'Content-Type': 'multipart/related; boundary=$MULTIPART_REQUESTS_BOUNDARY_STRING',
'Content-Length': multiPartRequestBodyString.length.toString(),
//'If-Match': 'my_etag_here_when_updating_existing_file_with_put',
});
fileStreamedRequest.sink.add(utf8.encode(multiPartRequestBodyString));
fileStreamedRequest.sink.close();
final http.StreamedResponse httpPostResponse = await fileStreamedRequest.send();
// Do what you want with the response too
//...
}