Dart: Processing an uploaded file on the server from client request - dart

I am trying to process images uploaded from client on the server but am receiving the following errors. Am I processing the httpRequest wrong?
Unhandled exception:
Uncaught Error: HttpException: Connection closed while receiving data, uri = /api/upload
/// Client Code (dart file) (Works # sending request)
sendData(dynamic data) {
final req = new HttpRequest();
FormData fd = new FormData();
fd.append('uploadContent', data);
fd.appendBlob('uploadContent', data);
req.open("POST", "http://127.0.0.1:8080/api/upload", async: true);
req.send(fd);
req.onReadyStateChange.listen((Event e) {
if (req.readyState == HttpRequest.DONE &&
(req.status == 200 || req.status == 0)) {
window.alert("upload complete");
}
});
}
InputElement uploadInput = document.querySelector('#sideBar-profile-picture');
uploadInput.onChange.listen((Event event){
// read file content as dataURL
final files = uploadInput.files;
if (files.length == 1) {
File file = files[0];
FileReader reader = new FileReader();
reader.onLoad.listen((e) {
print('results: ${reader.result}');
sendData(reader.result);
});
reader.readAsArrayBuffer(file);
}
});
I have a small server listening for the request (/api/upload) and calling handleUploadRequest with the httpRequest being passed in as the param.
Server code (This is where I am stuck)
Future<Null> handleUploadRequest(final HttpRequest httpRequest) async {
httpRequest.fold(new BytesBuilder(), (b, d) => b..add(d)).then((builder) {
var data = builder.takeBytes();
print('bytes builder: ${data}');
});
}
I am trying to read the data so that I can store it on a cdn but never get a chance to since the connection always gets closed while receiving the data.
Any help on being able to complete this is appreciated. Been at this for the past couple days:/

It is hard to tell when/if you close the httpRequest. If you are doing it right after handleUploadRequest returns, it will indeed close the connection as you are not waiting for httpRequest.fold() to complete. Adding await as shown below and making sure to call httpRequest.close() after this function complete asynchronously should work
Future<Null> handleUploadRequest(final HttpRequest httpRequest) async {
await httpRequest.fold(new BytesBuilder(), (b, d) => b..add(d)).then((builder) {
var data = builder.takeBytes();
print('bytes builder: ${data}');
});
}
(FYI) I have a similar code that works when testing with curl and uploading a file

Related

Dart server side: How to receive data from the Postman (form-data)?

I am using dart:io to create the server. I send the request from the Postman with form-data. I need to use form-data because my old API from another language uses it and the app uses it too.
At the moment. I am trying to get the data and files with this code:
Future main(List<String> arguments) async {
HttpServer server = await HttpServer.bind('localhost', 8085);
server.listen((HttpRequest request) async {
String jsonString = await request.cast<List<int>>().transform(utf8.decoder).join();
print("jsonString:\n$jsonString");
await request.response.close();
});
}
When I send the data and a file from the Postman with this below.
I will get the error below.
Unhandled exception:
FormatException: Unexpected extension byte (at offset 435)
If I don't send the file as image 1, I got this.
jsonString:
----------------------------166099235909119466948633
Content-Disposition: form-data; name="key 1"
Content-Type: application/json
value 1
----------------------------166099235909119466948633
Content-Disposition: form-data; name="key 2"
value 2
----------------------------166099235909119466948633--
I can't convert the above results to variables.
I don't know how to do that. Has anyone an example for doing this or suggest any package to me? This is my first time creating a dart server.
I follow this.
You can get the data and files from the request by using shelf_multipart (Other packages may be used in conjunction with this one and find more methods on GitHub).
If you want to see results quickly that it can be done. Follow this below.
I am using 3 packages including the shelf, shelf_router, and shelf_multipart packages.
You need to add these packages to your pubspec.yaml.
(You can copy and paste these into your pubspec.yaml.)
dependencies:
shelf: ^1.4.0
shelf_router: ^1.1.3
shelf_multipart: ^1.0.0
Then copy my code and past it to your main.dart:
import 'dart:convert';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf_multipart/form_data.dart';
import 'package:shelf_multipart/multipart.dart';
Future main(List<String> arguments) async {
final service = Service();
final server = await shelf_io.serve(service.handler, 'localhost', 8085);
print('Server running on localhost:${server.port}');
}
class Service {
Handler get handler {
final router = Router();
router.post("/example", (Request request) async {
if (request.isMultipart && request.isMultipartForm) {
Map<String, dynamic>? data = await RequestConverter.formData(request);
return data != null
? Response.ok("form-data: true, receive-data: true, data: $data")
: Response.ok("form-data: true, receive-data: false");
}
return Response.ok("form-data: false");
});
router.all('/<ignored|.*>', (Request request) {
return Response.notFound('Page not found');
});
return router;
}
}
class RequestConverter {
static Future<Map<String, dynamic>?> formData(Request request) async {
try {
Map<String, dynamic> data = {};
Map<String, dynamic> files = {};
final List<FormData> formDataList = await request.multipartFormData.toList();
for (FormData formData in formDataList) {
if (formData.filename == null) {
String dataString = await formData.part.readString();
data[formData.name] = Json.tryDecode(dataString) ?? dataString; //Postman doesn't send data as json
} else if (formData.filename is String) {
files[formData.name] = await formData.part.readBytes();
}
}
return {"data": data, "files": files};
} catch (e) {
return null;
}
}
}
class Json {
static String? tryEncode(data) {
try {
return jsonEncode(data);
} catch (e) {
return null;
}
}
static dynamic tryDecode(data) {
try {
return jsonDecode(data);
} catch (e) {
return null;
}
}
}
After this, you can start your server in your terminal. For me I am using:
dart run .\bin\main.dart
Finally, open the Postman and paste http://localhost:8085/example to the URL field, select the POST method, and form-data. You can add the data into the KEY and VALUE fields. Then press send.
This is my example in the Postman:
This solution work with http.MultipartRequest() from the Flutter app.

uploading files to a quart based server

I'm trying to convert my Flask server to Quart.
I have a form with file inputs in it, which I send over to the server. It used to work perfectly with Flask, but now I fail to send the files over.
Here's the code which worked:
app = Flask(__name__)
#app.route('/upload', methods=('POST',))
def process_form_data():
for name, file in request.files.items():
print(f'Processing {name}: {len(file.read())}')
return make_response(jsonify({"message": "File uploaded"}), 200)
The sending code is:
const request = new XMLHttpRequest();
function check_and_post(e) {
const formElement = e.target;
request.addEventListener("load", function (e) {
if (request.status == 200) {
window.location.href = '/work';
}
else {
alert('Error uploading file');
console.log(e);
}
});
// request error handler
request.addEventListener("error", function (e) {
alert('Error uploading files, try again.');
});
request.open("post", formElement.action);
request.responseType = "json";
request.send(new FormData(formElement));
}
My flask code consumed the files, and the load event was received with status 200.
My Quart code is as follows:
app = Quart(__name__)
#app.route('/upload', methods=('POST',))
async def process_form_data():
for name, file in (await request.files).items():
print(f'Processing {name}: {len(file.read())}')
return make_response(jsonify({"message": "File uploaded"}), 200)
Now I get to the "Error uploading file" branch, i.e. the request status received for the progress event isn't 200 (I didn't get any request for the error event).
The status of the request I now get in load is 413 (request too large?). The server side does not log any exception or warning.
Any idea?
just in case someone lese search for this:
Quart has a configurable property that let you to set the maximum size of your requests payload (MAX_CONTENT_LENGTH) to set that use the below command in
max_mb= 50
app.config['MAX_CONTENT_LENGTH'] = mb *1000 * 1024 # for 50 megabyte

Processing an Image in Dart

I am currently trying to process an image on the server that was uploaded by a user. My goal is to take this image that was uploaded, process it on the server and then upload it to rackspace cloud files. I have had no luck in finding a way. I am hoping that someone can lead me into the correct direction.
Heres what I have so far on the server side for processing
Future < Null > handleUploadRequest(final HttpRequest httpRequest) async {
print('handle upload -------------------------');
var data;
await httpRequest.fold(new BytesBuilder(), (b, d) => b..add(d)).then((builder) {
data = builder.takeBytes();
String encodedData = JSON.encode(data);
int dataLength = encodedData.length;
// Uploading image to rackspace cloud files
// Here we have to send a request with username & api key to get an auth token
http.post(tokens, headers : {'Content-Type':'application/json'}, body: JSON.encode({"auth": {"RAX-KSKEY:apiKeyCredentials":{"username":"XXXXXXXXXXXXXXX","apiKey":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}}})).then((response) {
print("Response status: ${response.statusCode}");
print("Response body: ${response.body}");
return response;
}).then((response){
authResponse = JSON.decode(response.body);
String token = authResponse['access']['token']['id'];
/////////// This is where we upload the image to Rackspace ///////
var request = new http.MultipartRequest("PUT", Uri.parse('https://storage101.iad3.clouddrive.com/v1/MossoCloudFS_XXXXXXX/TestContainer/testimage.jpg'));
request.headers['X-Auth-Token'] = token;
request.headers['Content-Type'] = 'image/jpeg';
var stream = new http.ByteStream.fromBytes(data);
request.files.add(new http.MultipartFile('file', stream, data.length, filename: 'testfile.jpg', contentType: 'image/jpeg');
print('requesT: ${request}');
request.send().then((response) {
print(response.statusCode);
print(response.headers);
print(response.stream);
if (response.statusCode == 200) print("Uploaded!");
});
//// End of Upload to Rackspace //////////
print('Upload Complete!');
httpRequest.response.write(data);
await httpRequest.response.close();
}
The only issue right now is that in https://pub.dartlang.org/packages/http, I need to call the type MediaType in the parameter content-type. I have no idea how to call this. It seems like it is a factory inside a class? If I do not call a content-type then it defaults to octet-stream which cannot be opened from the cdn storage container.
Reference to this way of uploading is from How can I upload a PDF using Dart's HttpClient?
Looks like an missing return before http.put(...) and httpRequest.response.close();
Either you use await before each async call (call to functions which return a Future) or return each such Future to the caller to preserve order of execution. Without any of these the async execution is scheduled for later execution and the next line of your code is executed instead before the async code you called even started.
Update pure async/await implementation
import 'package:http/http.dart' as http;
import 'dart:async' show Future, Stream;
import 'dart:io';
Future<Null> handleUploadRequest(final HttpRequest httpRequest) async {
print('handle upload -------------------------');
print('httpRequest: ${httpRequest.headers}');
var data;
var builder = await httpRequest.fold(new BytesBuilder(), (b, d) => b..add(d));
data = builder.takeBytes();
print('bytes builder: ${data}');
// Uploading image to rackspace cloud files
var url =
'https://storage101.dfw1.clouddrive.com/v1/{accountnumber}/{container}/';
var response = await http.put(url,
headers: {
'X-Auth-Token': '{XXXXXXXXXAPI_KEYXXXXXXXXXX}',
'Content-Type': 'image/jpeg',
'Content-Length': '${httpRequest.headers.contentLength}',
},
body: data);
print("Response status: ${response.statusCode}");
print("Response body: ${response.body}");
print('closing connection with data complete');
httpRequest.response.write(data);
await httpRequest.response.close();
}

Reusable Sync HttpClient Request

I need a reusable function that makes an HTTP request and awaits for its completion before returning the response as a String.
Here's the main function:
main() async {
var json;
json = await makeRequest('https://...');
print(json);
print('*** request complete ***');
}
(First Case) This is the reusable function that makes the HTTP request:
makeRequest(String url) async {
var request = await new HttpClient().postUrl(Uri.parse(url));
// Includes the access token in the request headers.
request.headers.add(...);
// Waits until the request is complete.
var response = await request.close();
await for (var contents in response.transform(UTF8.decoder)) {
return contents;
}
}
This works as expected and the output is:
// Response contents as a String...
*** request complete ***
(Second Case) Then I tried to do this and it didn't work:
makeRequest(String url) async {
var request = await new HttpClient().postUrl(Uri.parse(url));
// Includes the access token in the request headers.
request.headers.add(...);
// Waits until the request is complete.
var response = await request.close();
var json = '';
await response.transform(UTF8.decoder).listen((contents) {
// At first I tried to return contents here, but then I added onDone().
json += contents;
}, onDone: () {
return json;
});
return json;
}
I've tried defining the function within listen with async and await, returning contents within listen without onDone(), but the output is the same:
// Empty line.
*** request complete ***
// Waits a few seconds doing nothing before terminating...
Does anyone know why the second case doesn't work?
EDIT:
After updating the code it does what it was supposed to do, but takes a few seconds before terminating execution:
Future<String> twitterRequest(String url) async {
var request = await new HttpClient().postUrl(Uri.parse(url));
// Includes the access token in the request headers.
request.headers.add(...);
// Waits until the request is complete.
var response = await request.close();
var json = '';
await for (var contents in response.transform(UTF8.decoder)) {
json += contents;
// Putting a break here produces the same output but terminates immediately (as wanted).
}
return json;
}
Output:
// Prints response contents...
*** request complete ***
// Takes a few seconds before execution terminates. With the break the code terminates immediately.
EDIT2:
After submitting this issue on GitHub, I found out that instances of the HttpClient have a connection pool and keep persistent connections by default, which keeps the Dart VM alive. Please consult the issue page to find out about the possible solutions.
It's probably caused by the await before the response.transform.
You might want something like
return response.transform(UTF8.decoder).join('');
The pause is not related to makeRequest(). The Dart VM seems to wait for something before it exits. Adding exit(0); as last line in main() makes the application exit immediately.
Update
According to the response on the Dart SDK issue
This is caused by the HttpClient instance having a connection pool
which can keep the Dart VM alive. There are two ways of avoiding this:
1) Close the HttpClient explicitly
2) Use non-persistent connections
import 'dart:async';
import 'dart:convert' show UTF8;
import 'dart:io';
Future main() async {
await makeRequest();
print('end of main');
}
Future makeRequest() async {
var client = new HttpClient();
var request = await client.postUrl(Uri.parse('https://example.com'));
var response = await request.close();
var contents = await response.transform(UTF8.decoder).join();
print(contents);
client.close(); // Close the client.
}
import 'dart:async';
import 'dart:convert' show UTF8;
import 'dart:io';
Future main() async {
await makeRequest();
print('end of main');
}
Future makeRequest() async {
var request = await new HttpClient().postUrl(Uri.parse('https://example.com'));
request.persistentConnection = false; // Use non-persistent connection.
var response = await request.close();
var contents = await response.transform(UTF8.decoder).join();
print(contents);
}

How can I upload a PDF using Dart's HttpClient?

I need to post a PDF file to a remote REST API, and I can't for the life of me figure it out. No matter what I do, the server responds that I have not yet associated an object with the file parameter. Let's say that I have a PDF called test.pdf. This is what I've been doing so far:
// Using an HttpClientRequest named req
req.headers.contentType = new ContentType('application', 'x-www-form-urlencoded');
StringBuffer sb = new StringBuffer();
String fileData = new File('Test.pdf').readAsStringSync();
sb.write('file=$fileData');
req.write(sb.toString());
return req.close();
Thus far, I've tried virtually every combination and encoding of the data that I write() to the request, but to no avail. I've tried sending it as codeUnits, I've tried encoding it using a UTF8.encode, I've tried encoding it using a Latin1Codec, everything. I'm stumped.
Any help would be greatly appreciated.
You can use MultipartRequest from the http package :
var uri = Uri.parse("http://pub.dartlang.org/packages/create");
var request = new http.MultipartRequest("POST", url);
request.fields['user'] = 'john#doe.com';
request.files.add(new http.MultipartFile.fromFile(
'package',
new File('build/package.tar.gz'),
contentType: new ContentType('application', 'x-tar'));
request.send().then((response) {
if (response.statusCode == 200) print("Uploaded!");
});
Try using the multipart/form-data header rather than x-www-form-urlencoded. This should be used for binary data, also can you show your full req request?
void uploadFile(File file) async {
// string to uri
var uri = Uri.parse("enter here upload URL");
// create multipart request
var request = new http.MultipartRequest("POST", uri);
// if you need more parameters to parse, add those like this. i added "user_id". here this "user_id" is a key of the API request
request.fields["user_id"] = "text";
// multipart that takes file.. here this "idDocumentOne_1" is a key of the API request
MultipartFile multipartFile = await http.MultipartFile.fromPath(
'idDocumentOne_1',
file.path
);
// add file to multipart
request.files.add(multipartFile);
// send request to upload file
await request.send().then((response) async {
// listen for response
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}).catchError((e) {
print(e);
});
}
I used file picker to pick file.
Here is the codes for pick file.
Future getPdfAndUpload(int position) async {
File file = await FilePicker.getFile(
type: FileType.custom,
allowedExtensions: ['pdf','docx'],
);
if(file != null) {
setState(() {
file1 = file; //file1 is a global variable which i created
});
}
}
here file_picker flutter library.

Resources