Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Make MultipartFile recoverable to enable retrying FormData requests #1889

Merged
merged 9 commits into from
Jul 14, 2023
3 changes: 2 additions & 1 deletion dio/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ See the [Migration Guide][] for the complete breaking changes list.**
## Unreleased

- Remove `http` from `dev_dependencies`.
- Add support for restoring MultipartFile from `FormData`.
kuhnroyal marked this conversation as resolved.
Show resolved Hide resolved

## 5.2.1+1

Expand All @@ -28,7 +29,7 @@ See the [Migration Guide][] for the complete breaking changes list.**
Dio 6.0.0 - Please use the replacement `IOHttpClientAdapter.createHttpClient` instead.
- Using `CancelToken` no longer closes and re-creates `HttpClient` for each request when `IOHttpClientAdapter` is used.
- Fix timeout handling for browser `receiveTimeout`.
- Improve performance when sending binary data (`List<int>`/`Uint8List`).
- Improve performance when sending binary data (`List<int>`/`Uint8List`).

## 5.1.2

Expand Down
10 changes: 10 additions & 0 deletions dio/lib/src/multipart_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,14 @@ class MultipartFile {
_isFinalized = true;
return _stream;
}

/// Restore MultipartFile, returning a new instance of the same object. This is useful if your request failed and you wish
/// to sistematically retry it, for example a 401 error that can be solved by refreshing the token.
Copy link
Member

@AlexV525 AlexV525 Jul 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update codes to follow the 80-word length format or semantic line breaks.

Suggested change
/// Restore MultipartFile, returning a new instance of the same object. This is useful if your request failed and you wish
/// to sistematically retry it, for example a 401 error that can be solved by refreshing the token.
/// Restore MultipartFile, returning a new instance of the same object.
/// This is useful if your request failed and you wish to retry it,
/// such as an unauthorized exception can be solved by refreshing the token.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I applied the line length rules to all the changes I made, thanks

MultipartFile restoreMultipartFile() => MultipartFile(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure restore is a good word here because it represents manipulating the original object but here we produce a new one. Any other ideas? Other words like copy or duplicate might fit more in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

humm, how about reset?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed it as duplicateMultipartFile, but let me know if you think reset would fit better :)

_stream,
length,
filename: filename,
contentType: contentType,
headers: headers,
);
}
67 changes: 67 additions & 0 deletions dio/test/formdata_test.dart
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new method seems not tested though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the tests to comply to this, could you please check it out? a22208d

Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,73 @@ void main() async {
testOn: 'vm',
);

// Restored multipart files should be able to read again and be the same as the original ones.
test(
'complex with restoration',
() async {
final multipartFile1 = MultipartFile.fromString(
'hello world.',
headers: {
'test': <String>['a']
},
);
final multipartFile2 = await MultipartFile.fromFile(
'test/mock/_testfile',
filename: '1.txt',
headers: {
'test': <String>['b']
},
);
final multipartFile3 = MultipartFile.fromFileSync(
'test/mock/_testfile',
filename: '2.txt',
headers: {
'test': <String>['c']
},
);

final fm = FormData.fromMap({
'name': 'wendux',
'age': 25,
'path': '/图片空间/地址',
'file': multipartFile1,
'files': [
multipartFile2,
multipartFile3,
]
});
final fmStr = await fm.readAsBytes();

final fm1 = FormData();
fm1.fields.add(MapEntry('name', 'wendux'));
fm1.fields.add(MapEntry('age', '25'));
fm1.fields.add(MapEntry('path', '/图片空间/地址'));
fm1.files.add(
MapEntry(
'file',
multipartFile1.restoreMultipartFile(),
),
);
fm1.files.add(
MapEntry(
'files',
multipartFile2.restoreMultipartFile(),
),
);
fm1.files.add(
MapEntry(
'files',
multipartFile3.restoreMultipartFile(),
),
);
expect(fmStr.length, fm1.length);
expect(fm1.files[0].value.isFinalized, false);
expect(fm1.files[1].value.isFinalized, false);
expect(fm1.files[2].value.isFinalized, false);
},
testOn: 'vm',
);

test('encodes maps correctly', () async {
final fd = FormData.fromMap(
{
Expand Down