chopper
chopper copied to clipboard
How to I upload multipart image to server? I Tried every possible solutions reported in in issues but none helped :/
Below is the postman api.


Hi! Please try the https://stackoverflow.com/questions/59791332/upload-image-with-chopper answer
@SalilLuley Also, take a look at this comment: https://github.com/lejard-h/chopper/issues/55#issuecomment-575107144
I tried the above solutions, below is my api please check
@Post(
path: 'api/update',
headers: {"application": "x-www-form-urlencoded'"},
)
@multipart
Future<Response<BuiltProfilePictureResponse>> updateProfilePicture({
@Part("field") String field,
@PartFile("file") MultipartFile file,
@Header("Authorization") String token,
});
static ProfileService create() {
final client = ChopperClient(
baseUrl: BASE_URL,
services: [_$ProfileService()],
converter: BuiltValueConverter(),
interceptors: [
HttpLoggingInterceptor(),
]);
return _$ProfileService(client);
}
Api call
MultipartFile file = await MultipartFile.fromPath(
'profile_picture', _image.path,
contentType: new MediaType('image', 'jpg'));
final response = await _profileService.updateProfilePicture(
field: "profile_picture", file: file, token: "Bearer $token");
Still this throws error. :/
Can you please share the error and stack trace you're getting? It would help us narrow down the possible causes. 😉
If is throwing error in convertRequest method below on request.body. Says body is null.
Error Message E/flutter ( 1336): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: NoSuchMethodError: The getter 'types' was called on null. E/flutter ( 1336): Receiver: null E/flutter ( 1336): Tried calling: types
Below is the error SS.
Built BuiltValueConverter class
class BuiltValueConverter extends JsonConverter {
@override
Request convertRequest(Request request) {
return super.convertRequest(
request.copyWith(
// request.body is of type dynamic, but we know that it holds only BuiltValue classes (BuiltPost).
// Before sending the request to the network, serialize it to a List/Map using a BuiltValue serializer.
body: serializers.serializeWith(
// Since convertRequest doesn't have a type parameter, Serializer's type will be determined at runtime
serializers.serializerForType(request.body.runtimeType as Type),
request.body,
),
),
);
}
@override
Response<BodyType> convertResponse<BodyType, SingleItemType>(
Response response) {
// The response parameter contains raw binary JSON data by default.
// Utilize the already written code which converts this data to a dynamic Map or a List of Maps.
final Response dynamicResponse = super.convertResponse(response);
// customBody can be either a BuiltList<SingleItemType> or just the SingleItemType (if there's no list).
final BodyType customBody =
_convertToCustomObject<SingleItemType>(dynamicResponse.body)
as BodyType;
// Return the original dynamicResponse with a no-longer-dynamic body type.
return dynamicResponse.copyWith<BodyType>(body: customBody);
}
dynamic _convertToCustomObject<SingleItemType>(dynamic element) {
// If the type which the response should hold is explicitly set to a dynamic Map,
// there's nothing we can convert.
if (element is SingleItemType) return element;
if (element is List)
return _deserializeListOf<SingleItemType>(element);
else
return _deserialize<SingleItemType>(element as Map<String, dynamic>);
}
BuiltList<SingleItemType> _deserializeListOf<SingleItemType>(
List dynamicList,
) {
// Make a BuiltList holding individual custom objects
return BuiltList<SingleItemType>(
dynamicList.map((element) =>
_deserialize<SingleItemType>(element as Map<String, dynamic>)),
);
}
SingleItemType _deserialize<SingleItemType>(
Map<String, dynamic> value,
) {
// We have a type parameter for the BuiltValue type
// which should be returned after deserialization.
return serializers.deserializeWith<SingleItemType>(
serializers.serializerForType(SingleItemType)
as Serializer<SingleItemType>,
value,
);
}
}
Below is the response class
abstract class BuiltProfilePictureResponse
implements
Built<BuiltProfilePictureResponse, BuiltProfilePictureResponseBuilder> {
// fields go here
@nullable
int get status;
// @nullable
// BuiltProfilePictureResponseData get data;
@nullable
String get message;
BuiltProfilePictureResponse._();
factory BuiltProfilePictureResponse(
[updates(BuiltProfilePictureResponseBuilder b)]) =
_$BuiltProfilePictureResponse;
static Serializer<BuiltProfilePictureResponse> get serializer =>
_$builtProfilePictureResponseSerializer;
}
simple just use dio man
Having this same issue, any further resolutions?
Same fo me, any updates?
Having this same issue, any further resolutions?
Having this same issue, any further resolutions?
Unfortunately, we ended up just doing this one specific call in Dio =\
It is working for me like that:
@Put(
headers: {"Content-Type": "application/x-www-form-urlencoded"},
path: 'path')
@multipart
Future<Response> uploadAvatar(@PartFile('file') http.MultipartFile file;
final file = http.MultipartFile.fromBytes('file', bytes,
filename: file.path, contentType: MediaType('image', 'jpeg'));
uploadAvatar(file);
Guys? :)
Guys? :)
Hey thanks for this! This does work with one exception, instead of using the http.MultipartFile
I just had to import 'package:http/http.dart' show MultipartFile;
due to the generated file not working correctly otherwise, just incase someone in the future sees this thread
Thanks!
I managed to get it working properly, you just need to use the following approach:
@Post(
path: '/test',
headers: {
'Content-Type': 'application/form-data',
},
)
@Multipart()
Future<Response<FileAddResponseModel>> uploadFileWithField(
@Part('id') String id,
@PartFile('file') MultipartFile file,
@Part('description') String description,
);
And when loading the file on controller, you should use it like that:
final file = await http.MultipartFile.fromPath(
'file',
localfile.path!,
);
The localfile can be loaded as a PlatformFile.
Thanks all this works for me:
import 'dart:async';
import 'package:chopper/chopper.dart';
import 'package:http/http.dart' show MultipartFile; // <-- important
@ChopperApi(baseUrl: '')
abstract class MyApiService extends ChopperService {
// ...
@Post(
path: '/api/books',
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data', // <-- important
},
)
@Multipart()
Future<Response<String>> storeBook({
@Part('title') required String title,
@Part('description') required String description,
@PartFile('book_file') required MultipartFile bookFile,
});
// ...
}
import 'dart:io';
import 'package:chopper/chopper.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';
// ...
// filePathStr = '/data/user/0/com.example.myapp/cache/file_picker/my-book.pdf';
final bookFile = http.MultipartFile(
'book_file',
File(filePathStr).readAsBytes().asStream(),
File(filePathStr).lengthSync(),
filename: filePathStr.split('/').last,
contentType: MediaType.parse(
lookupMimeType(filePathStr) ?? '',
),
);
final response = await myApiService.storeBook(
title: 'Title1',
description: 'Description1',
bookFile: bookFile,
);
// ...