File support is nearly there.

This commit is contained in:
regiostech 2016-04-16 23:51:55 -04:00
parent 0e74bc9d9b
commit 1f37cd1b70
2 changed files with 67 additions and 7 deletions

View file

@ -12,6 +12,9 @@ class BodyParseResult {
/// The parsed query string.
Map query = {};
/// All files uploaded within this request.
List<Map> files = [];
}
/// Grabs data from an incoming request.
@ -39,14 +42,48 @@ Future<BodyParseResult> parseBody(HttpRequest request) async {
buildMapFromUri(result.query, queryMatch.group(1));
}
// Accept file
if (contentType != null && request.method == 'POST') {
RegExp parseBoundaryRgx = new RegExp(
r'^multipart\/form-data;\s*boundary=(.+)$');
if (parseBoundaryRgx.hasMatch(contentType.toString())) {
Match boundaryMatch = parseBoundaryRgx.firstMatch(contentType.toString());
String body = await request.transform(UTF8.decoder).join();
for (String chunk in body.split(boundaryMatch.group(1))) {
var fileData = getFileDataFromChunk(chunk);
if (fileData != null)
result.files.add(fileData);
}
}
}
return result;
}
/// Parses file data from a multipart/form-data chunk.
Map getFileDataFromChunk(String chunk) {
Map result = {};
RegExp isFormDataRgx = new RegExp(
r'Content-Dispositition:\s*form-data;\s*name="([^"]+)"');
if (isFormDataRgx.hasMatch(chunk)) {
result['name'] = isFormDataRgx.firstMatch(chunk).group(1);
RegExp contentTypeRgx = new RegExp(r'Content-Type:\s*([^\n]+)');
if (contentTypeRgx.hasMatch(chunk)) {
result['type'] = contentTypeRgx.firstMatch(chunk).group(1);
return result;
}
}
return null;
}
/// Parses a URI-encoded string into real data! **Wow!**
///
/// Whichever map you provide will be automatically populated from the urlencoded body string you provide.
buildMapFromUri(Map map, String body) {
RegExp parseArray = new RegExp(r'^(.+)\[\]$');
RegExp parseArrayRgx = new RegExp(r'^(.+)\[\]$');
for (String keyValuePair in body.split('&')) {
if (keyValuePair.contains('=')) {
@ -54,15 +91,15 @@ buildMapFromUri(Map map, String body) {
String key = Uri.decodeQueryComponent(split[0]);
String value = Uri.decodeQueryComponent(split[1]);
if (parseArray.hasMatch(key)) {
Match queryMatch = parseArray.firstMatch(key);
if (parseArrayRgx.hasMatch(key)) {
Match queryMatch = parseArrayRgx.firstMatch(key);
key = queryMatch.group(1);
if (!(map[key] is List)) {
map[key] = [];
}
map[key].add(getValue(value));
} else if(key.contains('.')) {
} else if (key.contains('.')) {
// i.e. map.foo.bar => [map, foo, bar]
List<String> keys = key.split('.');

View file

@ -38,7 +38,7 @@ main() {
print('GET $url/?hello=world');
var response = await client.get('$url/?hello=world');
print('Response: ${response.body}');
expect(response.body, equals('{"body":{},"query":{"hello":"world"}}'));
expect(response.body, equals('{"body":{},"query":{"hello":"world"},"files":[]}'));
});
test('GET Complex', () async {
@ -64,7 +64,7 @@ main() {
var response = await client.post(
url, headers: headers, body: 'hello=world');
print('Response: ${response.body}');
expect(response.body, equals('{"body":{"hello":"world"},"query":{}}'));
expect(response.body, equals('{"body":{"hello":"world"},"query":{},"files":[]}'));
});
test('Post Complex', () async {
@ -91,7 +91,7 @@ main() {
var response = await client.post(
url, headers: headers, body: postData);
print('Response: ${response.body}');
expect(response.body, equals('{"body":{"hello":"world"},"query":{}}'));
expect(response.body, equals('{"body":{"hello":"world"},"query":{},"files":[]}'));
});
test('Post Complex', () async {
@ -114,5 +114,28 @@ main() {
expect(body['map']['foo'], equals({'bar': 'baz'}));
});
});
group('File', () {
test('Single upload', () async {
String boundary = '----myBoundary';
Map headers = {
HttpHeaders.CONTENT_TYPE: 'multipart/form-data; boundary=$boundary'
};
String postData = '''
$boundary
Content-Dispositition: form-data; name="file"
Content-Type: text/plain
Hello world
$boundary--
''';
print('Form Data: \n$postData');
var response = await client.post(url, headers: headers, body: postData);
print('Response: ${response.body}');
var body = god.deserialize(response.body)['body'];
});
});
});
}