Jonatas

fix multipart files

  1 +import 'dart:io';
  2 +
1 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
2 import 'package:get/get.dart'; 4 import 'package:get/get.dart';
3 import 'routes/app_pages.dart'; 5 import 'routes/app_pages.dart';
4 import 'shared/logger/logger_utils.dart'; 6 import 'shared/logger/logger_utils.dart';
5 7
6 -void main() { 8 +void main() async {
  9 + final client = GetConnect();
  10 + final form = FormData({
  11 + 'file': MultipartFile(
  12 + File('README.md').readAsBytesSync(),
  13 + filename: 'readme.md',
  14 + ),
  15 + });
  16 + final response = await client.post('http://localhost:8080/upload', form);
  17 +
  18 + print(response.body);
  19 + WidgetsFlutterBinding.ensureInitialized();
7 runApp(MyApp()); 20 runApp(MyApp());
8 } 21 }
9 22
@@ -94,15 +94,17 @@ class GetHttpClient { @@ -94,15 +94,17 @@ class GetHttpClient {
94 List<int> bodyBytes; 94 List<int> bodyBytes;
95 BodyBytes bodyStream; 95 BodyBytes bodyStream;
96 final headers = <String, String>{}; 96 final headers = <String, String>{};
97 - headers['content-type'] = contentType ?? defaultContentType; 97 +
98 headers['user-agent'] = userAgent; 98 headers['user-agent'] = userAgent;
99 99
100 if (body is FormData) { 100 if (body is FormData) {
101 bodyBytes = await body.toBytes(); 101 bodyBytes = await body.toBytes();
102 headers['content-length'] = bodyBytes.length.toString(); 102 headers['content-length'] = bodyBytes.length.toString();
  103 + headers['content-type'] =
  104 + 'multipart/form-data; boundary=${body.boundary}';
103 } else if (body is Map || body is List) { 105 } else if (body is Map || body is List) {
104 var jsonString = json.encode(body); 106 var jsonString = json.encode(body);
105 - 107 + headers['content-type'] = contentType ?? defaultContentType;
106 //TODO check this implementation 108 //TODO check this implementation
107 if (contentType != null) { 109 if (contentType != null) {
108 if (contentType.toLowerCase() == 'application/x-www-form-urlencoded') { 110 if (contentType.toLowerCase() == 'application/x-www-form-urlencoded') {
@@ -114,6 +116,7 @@ class GetHttpClient { @@ -114,6 +116,7 @@ class GetHttpClient {
114 bodyBytes = utf8.encode(jsonString); 116 bodyBytes = utf8.encode(jsonString);
115 headers['content-length'] = bodyBytes.length.toString(); 117 headers['content-length'] = bodyBytes.length.toString();
116 } else if (body == null) { 118 } else if (body == null) {
  119 + headers['content-type'] = contentType ?? defaultContentType;
117 headers['content-length'] = '0'; 120 headers['content-length'] = '0';
118 } else { 121 } else {
119 if (!errorSafety) { 122 if (!errorSafety) {
@@ -126,7 +129,7 @@ class GetHttpClient { @@ -126,7 +129,7 @@ class GetHttpClient {
126 } 129 }
127 130
128 final uri = _createUri(url, query); 131 final uri = _createUri(url, query);
129 - 132 + print(headers);
130 return Request( 133 return Request(
131 method: method, 134 method: method,
132 url: uri, 135 url: uri,
1 import 'dart:async'; 1 import 'dart:async';
2 import 'dart:convert'; 2 import 'dart:convert';
3 import 'dart:math'; 3 import 'dart:math';
4 -import '../../../../get_rx/src/rx_stream/rx_stream.dart';  
5 import '../request/request.dart'; 4 import '../request/request.dart';
6 import '../utils/utils.dart'; 5 import '../utils/utils.dart';
7 import 'multipart_file.dart'; 6 import 'multipart_file.dart';
8 7
9 class FormData { 8 class FormData {
10 FormData(Map<String, dynamic> map) : boundary = _getBoundary() { 9 FormData(Map<String, dynamic> map) : boundary = _getBoundary() {
11 - urlEncode(map, '', false, (key, value) {  
12 - if (value == null) return;  
13 - (value is MultipartFile)  
14 - ? files.add(MapEntry(key, value))  
15 - : fields.add(MapEntry(key, value.toString()));  
16 - return; 10 + map.forEach((key, value) {
  11 + if (value == null) return null;
  12 + if (value is MultipartFile) {
  13 + files.add(MapEntry(key, value));
  14 + } else if (value is List<MultipartFile>) {
  15 + files.addAll(value.map((e) => MapEntry(key, e)));
  16 + } else {
  17 + fields.add(MapEntry(key, value.toString()));
  18 + }
17 }); 19 });
18 } 20 }
19 21
@@ -87,25 +89,27 @@ class FormData { @@ -87,25 +89,27 @@ class FormData {
87 } 89 }
88 90
89 Future<List<int>> toBytes() { 91 Future<List<int>> toBytes() {
90 - final getStream = GetStream<List<int>>();  
91 -  
92 - for (final item in fields) {  
93 - stringToBytes('--$boundary\r\n', getStream);  
94 - stringToBytes(_fieldHeader(item.key, item.value), getStream);  
95 - stringToBytes(item.value, getStream);  
96 - writeLine(getStream); 92 + return BodyBytes(_encode()).toBytes();
97 } 93 }
98 94
99 - Future.forEach<MapEntry<String, MultipartFile>>(files, (file) {  
100 - stringToBytes('--$boundary\r\n', getStream);  
101 - stringToBytes(_fileHeader(file), getStream); 95 + Stream<List<int>> _encode() async* {
  96 + const line = [13, 10];
  97 + final separator = utf8.encode('--$boundary\r\n');
  98 + final close = utf8.encode('--$boundary--\r\n');
102 99
103 - return streamToFuture(file.value.stream, getStream)  
104 - .then((_) => writeLine(getStream));  
105 - }).then((_) {  
106 - stringToBytes('--$boundary--\r\n', getStream);  
107 - getStream.close();  
108 - });  
109 - return BodyBytes(getStream.stream).toBytes(); 100 + for (var field in fields) {
  101 + yield separator;
  102 + yield utf8.encode(_fieldHeader(field.key, field.value));
  103 + yield utf8.encode(field.value);
  104 + yield line;
  105 + }
  106 +
  107 + for (final file in files) {
  108 + yield separator;
  109 + yield utf8.encode(_fileHeader(file));
  110 + yield* file.value.stream;
  111 + yield line;
  112 + }
  113 + yield close;
110 } 114 }
111 } 115 }
1 import 'dart:async'; 1 import 'dart:async';
2 import 'dart:convert'; 2 import 'dart:convert';
3 -import '../../../../get_rx/src/rx_stream/rx_stream.dart';  
4 import '../request/request.dart'; 3 import '../request/request.dart';
5 4
6 bool isTokenChar(int byte) { 5 bool isTokenChar(int byte) {
@@ -66,67 +65,13 @@ final newlineRegExp = RegExp(r'\r\n|\r|\n'); @@ -66,67 +65,13 @@ final newlineRegExp = RegExp(r'\r\n|\r|\n');
66 /// characters. 65 /// characters.
67 bool isPlainAscii(String string) => _asciiOnly.hasMatch(string); 66 bool isPlainAscii(String string) => _asciiOnly.hasMatch(string);
68 67
69 -StringBuffer urlEncode(  
70 - dynamic sub,  
71 - String path,  
72 - bool encode,  
73 - String Function(String key, Object value) handler,  
74 -) {  
75 - var urlData = StringBuffer('');  
76 - var leftBracket = '[';  
77 - var rightBracket = ']';  
78 -  
79 - if (encode) {  
80 - leftBracket = '%5B';  
81 - rightBracket = '%5D';  
82 - }  
83 -  
84 - var encodeComponent = encode ? Uri.encodeQueryComponent : (e) => e;  
85 - if (sub is Map) {  
86 - sub.forEach((key, value) {  
87 - if (path == '') {  
88 - urlEncode(  
89 - value,  
90 - '${encodeComponent(key as String)}',  
91 - encode,  
92 - handler,  
93 - );  
94 - } else {  
95 - urlEncode(  
96 - value,  
97 - '$path$leftBracket${encodeComponent(key as String)}$rightBracket',  
98 - encode,  
99 - handler,  
100 - );  
101 - }  
102 - });  
103 - } else {  
104 - throw 'FormData need be a Map';  
105 - }  
106 -  
107 - return urlData;  
108 -}  
109 -  
110 const String GET_BOUNDARY = 'getx-http-boundary-'; 68 const String GET_BOUNDARY = 'getx-http-boundary-';
111 69
112 -Future streamToFuture(Stream stream, GetStream sink) {  
113 - var completer = Completer();  
114 - stream.listen(sink.add,  
115 - onError: sink.addError, onDone: () => completer.complete());  
116 - return completer.future;  
117 -}  
118 -  
119 -void stringToBytes(String string, GetStream stream) {  
120 - stream.add(utf8.encode(string));  
121 -}  
122 -  
123 /// Encode [value] like browsers 70 /// Encode [value] like browsers
124 String browserEncode(String value) { 71 String browserEncode(String value) {
125 return value.replaceAll(newlineRegExp, '%0D%0A').replaceAll('"', '%22'); 72 return value.replaceAll(newlineRegExp, '%0D%0A').replaceAll('"', '%22');
126 } 73 }
127 74
128 -void writeLine(GetStream stream) => stream.add([13, 10]);  
129 -  
130 const List<int> boundaryCharacters = <int>[ 75 const List<int> boundaryCharacters = <int>[
131 43, 76 43,
132 95, 77 95,