Outline
Sometimes, you need to upload files using the http package in Flutter. In this case, you can upload files using MultipartRequest
in the http package. In this blog post, I will introduce how to upload files using MultipartRequest
and how to test it.
MultipartRequest to upload files
The following is an example of uploading files using the MultipartRequest
in the http package.
class ExampleAPI {
final String token;
final http.MultipartRequest httpClient;
ExampleAPI({
required this.token,
@visibleForTesting http.MultipartRequest? mockClient,
}) : httpClient = mockClient ??
http.MultipartRequest(
"POST",
Uri.parse('${ENV.apiServer}/api/app'),
);
Future<InfoData> sendData({
required DateTime date,
required int status,
required int docType,
required String note,
required String passportImage,
String? certificationImage,
List<String>? receiptImages,
}) async {
Map<String, String> data = {};
data['date'] = dateFormatForSearch(date);
data['status'] = '$status';
data['doc_type'] = '$docType';
data['note'] = note;
List<MultipartFile> files = [];
files.add(await http.MultipartFile.fromPath('passport_image', passportImage));
if (jpnCertImg != null && jpnCertImg != '') {
files.add(await http.MultipartFile.fromPath('cert_img', certificationImage));
}
if (receiptImages?.isNotEmpty == true) {
for (var image in receiptImages!) {
if (image == '') continue;
files.add(await http.MultipartFile.fromPath('receipt_img[]', image));
}
}
httpClient.headers.addAll({'Authorization': 'Bearer $token'});
httpClient.fields.addAll(data);
httpClient.files.addAll(files);
final stream = await httpClient.send();
return http.Response.fromStream(stream).then((response) {
final data = jsonDecode(utf8.decode(response.bodyBytes));
if (data['success'] == true) {
return InfoData.fromJson(data['data']);
} else {
throw Exception('Unknown response');
}
});
}
}
Let’s take a closer look at the part of uploading files using the MultipartRequest
in the http package.
ExampleAPI({
required this.token,
@visibleForTesting http.MultipartRequest? mockClient,
}) : httpClient = mockClient ??
http.MultipartRequest(
"POST",
Uri.parse('${ENV.apiServer}/api/app'),
);
To upload files using the MultipartRequest
, you first need to create an instance of http.MultipartRequest
.
Map<String, String> data = {};
data['date'] = dateFormatForSearch(date);
data['status'] = '$status';
data['doc_type'] = '$docType';
data['note'] = note;
The MultipartRequest
can send not only files but also other information. So, I prepared other information to send together.
List<MultipartFile> files = [];
files.add(await http.MultipartFile.fromPath('passport_image', passportImage));
if (jpnCertImg != null && jpnCertImg != '') {
files.add(await http.MultipartFile.fromPath('cert_img', certificationImage));
}
if (receiptImages?.isNotEmpty == true) {
for (var image in receiptImages!) {
if (image == '') continue;
files.add(await http.MultipartFile.fromPath('receipt_img[]', image));
}
}
The function, which sets the file to upload, receives the path of the file to upload. The path of the file to upload is converted to MultipartFile
using http.MultipartFile.fromPath
.
When uploading multiple files, you can send them as an array by using []
like receipt_img[]
.
httpClient.headers.addAll({'Authorization': 'Bearer $token'});
httpClient.fields.addAll(data);
httpClient.files.addAll(files);
Now, set the Bearer
token in the header to upload the file and add the prepared data to fields
. And add the prepared file to files
.
final stream = await httpClient.send();
return http.Response.fromStream(stream).then((response) {
final data = jsonDecode(utf8.decode(response.bodyBytes));
if (data['success'] == true) {
return InfoData.fromJson(data['data']);
} else {
throw Exception('Unknown response');
}
});
Finally, send the data and receive the response. Process the received response according to your app.
Test code
Next, let’s see how to test the function that uploads files using the MultipartRequest
in the http package.
First, the full code is as follows.
...
@GenerateMocks([http.MultipartRequest])
void main() {
final mockHttpMultipartRequest = CustomMockMultipartRequest();
setUp(() {
mockHttpMultipartRequest.headers.clear();
mockHttpMultipartRequest.fields.clear();
mockHttpMultipartRequest.files.clear();
when(mockHttpMultipartRequest.send()).thenAnswer(
(_) async {
final responseBody = jsonEncode({
'success': true,
'data': {
'amount': 1000,
'commission': 3000,
}
});
final stream = Stream.value(utf8.encode(responseBody));
return http.StreamedResponse(stream, 200);
},
);
});
test('Success', () async {
final result = await TaxRefundAPI(
token: 'test_token',
client: mockHttpMultipartRequest,
).sendData(
permitDate: DateTime.parse('2022-01-01 01:04'),
status: 91,
docType: 2,
note: '',
passportImg: 'assets/images/passport_sample.jpg',
certificationImage: 'assets/images/placeholder.png',
receiptImages: [
'assets/images/sample_receipt.png',
'assets/images/sample_receipt.png',
'assets/images/sample_receipt.png',
],
);
// Request parameters
expect(
mockHttpMultipartRequest.headers,
{'Authorization': 'Bearer test_token'},
);
expect(
mockHttpMultipartRequest.fields,
{
'date': '2022-01-01',
'status': '91',
'doc_type': '2',
'note': '',
},
);
expect(mockHttpMultipartRequest.files.length, 5);
expect(mockHttpMultipartRequest.files[0].field, 'passport_image');
expect(mockHttpMultipartRequest.files[0].filename, 'passport_sample.jpg');
expect(mockHttpMultipartRequest.files[1].field, 'cert_img');
expect(mockHttpMultipartRequest.files[1].filename, 'placeholder.png');
expect(mockHttpMultipartRequest.files[2].field, 'receipt_img[]');
expect(
mockHttpMultipartRequest.files[2].filename,
'sample_receipt.png',
);
expect(mockHttpMultipartRequest.files[3].field, 'receipt_img[]');
expect(
mockHttpMultipartRequest.files[3].filename,
'sample_receipt.png',
);
expect(mockHttpMultipartRequest.files[4].field, 'receipt_img[]');
expect(
mockHttpMultipartRequest.files[4].filename,
'sample_receipt.png',
);
// Response
expect(result, isA<InfoData>());
expect(result.toMap(), {
'amount': 1000,
'commission': 3000,
});
});
test('Throw error when response is failed', () async {
when(mockHttpMultipartRequest.send()).thenAnswer(
(_) async {
final responseBody = jsonEncode({
'success': false,
});
final stream = Stream.value(utf8.encode(responseBody));
return http.StreamedResponse(stream, 200);
},
);
try {
await TaxRefundAPI(
token: 'test_token',
client: mockHttpMultipartRequest,
).sendData(
permitDate: DateTime.parse('2022-01-01 01:04'),
status: 91,
docType: 2,
note: '',
passportImg: 'assets/images/passport_sample.jpg',
certificationImage: 'assets/images/placeholder.png',
receiptImages: [
'assets/images/sample_receipt.png',
'assets/images/sample_receipt.png',
'assets/images/sample_receipt.png',
],
);
} catch (e) {
expect(e.toString(), 'Exception: Unknown response');
}
});
}
class CustomMockMultipartRequest extends MockMultipartRequest {
@override
final Map<String, String> headers = {};
@override
final Map<String, String> fields = {};
@override
final List<MultipartFile> files = [];
}
Let’s take a closer look at the test code.
...
@GenerateMocks([http.MultipartRequest])
void main() {
final mockHttpMultipartRequest = CustomMockMultipartRequest();
setUp(() {
mockHttpMultipartRequest.headers.clear();
mockHttpMultipartRequest.fields.clear();
mockHttpMultipartRequest.files.clear();
when(mockHttpMultipartRequest.send()).thenAnswer(
(_) async {
final responseBody = jsonEncode({
'success': true,
'data': {
'amount': 1000,
'commission': 3000,
}
});
final stream = Stream.value(utf8.encode(responseBody));
return http.StreamedResponse(stream, 200);
},
);
});
...
}
class CustomMockMultipartRequest extends MockMultipartRequest {
@override
final Map<String, String> headers = {};
@override
final Map<String, String> fields = {};
@override
final List<MultipartFile> files = [];
}
I used DI(Dependency Injec)
, so I created a Mock
object for testing and initialized the Mock
object using the setUp
function.
...
@GenerateMocks([http.MultipartRequest])
void main() {
final mockHttpMultipartRequest = CustomMockMultipartRequest();
...
test('Success', () async {
final result = await TaxRefundAPI(
token: 'test_token',
client: mockHttpMultipartRequest,
).sendData(
permitDate: DateTime.parse('2022-01-01 01:04'),
status: 91,
docType: 2,
note: '',
passportImg: 'assets/images/passport_sample.jpg',
certificationImage: 'assets/images/placeholder.png',
receiptImages: [
'assets/images/sample_receipt.png',
'assets/images/sample_receipt.png',
'assets/images/sample_receipt.png',
],
);
// Request parameters
expect(
mockHttpMultipartRequest.headers,
{'Authorization': 'Bearer test_token'},
);
expect(
mockHttpMultipartRequest.fields,
{
'date': '2022-01-01',
'status': '91',
'doc_type': '2',
'note': '',
},
);
expect(mockHttpMultipartRequest.files.length, 5);
expect(mockHttpMultipartRequest.files[0].field, 'passport_image');
expect(mockHttpMultipartRequest.files[0].filename, 'passport_sample.jpg');
expect(mockHttpMultipartRequest.files[1].field, 'cert_img');
expect(mockHttpMultipartRequest.files[1].filename, 'placeholder.png');
expect(mockHttpMultipartRequest.files[2].field, 'receipt_img[]');
expect(
mockHttpMultipartRequest.files[2].filename,
'sample_receipt.png',
);
expect(mockHttpMultipartRequest.files[3].field, 'receipt_img[]');
expect(
mockHttpMultipartRequest.files[3].filename,
'sample_receipt.png',
);
expect(mockHttpMultipartRequest.files[4].field, 'receipt_img[]');
expect(
mockHttpMultipartRequest.files[4].filename,
'sample_receipt.png',
);
// Response
expect(result, isA<InfoData>());
expect(result.toMap(), {
'amount': 1000,
'commission': 3000,
});
});
...
}
...
By using the Mock
object created earlier, I tested the case where the request was successfully processed.
...
@GenerateMocks([http.MultipartRequest])
void main() {
final mockHttpMultipartRequest = CustomMockMultipartRequest();
...
test('Throw error when response is failed', () async {
when(mockHttpMultipartRequest.send()).thenAnswer(
(_) async {
final responseBody = jsonEncode({
'success': false,
});
final stream = Stream.value(utf8.encode(responseBody));
return http.StreamedResponse(stream, 200);
},
);
try {
await TaxRefundAPI(
token: 'test_token',
client: mockHttpMultipartRequest,
).sendData(
permitDate: DateTime.parse('2022-01-01 01:04'),
status: 91,
docType: 2,
note: '',
passportImg: 'assets/images/passport_sample.jpg',
certificationImage: 'assets/images/placeholder.png',
receiptImages: [
'assets/images/sample_receipt.png',
'assets/images/sample_receipt.png',
'assets/images/sample_receipt.png',
],
);
} catch (e) {
expect(e.toString(), 'Exception: Unknown response');
}
});
}
...
Lastly, I tested the case where the response failed.
Completed
Done! We’ve seen how to upload files using MultipartRequest
in the http package in Flutter and how to test it.
If you are implementing a file upload feature in Flutter, refer to this blog post and try implementing it and writing test code.
Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!
App promotion
Deku
.Deku
created the applications with Flutter.If you have interested, please try to download them for free.