In my Flutter application, I have to download multiple files and have to store it in application storage.
For that I am using Dio and added below plugin dependencies in pubspec.yaml file :
dio: any
path_provider: any
In main.dart, I have below variables :
String uri =
'https://i.picsum.photos/id/698/200/200.jpg?hmac=EElVlYPe8BAq1Btf4bWUxP9NoQP01_e8LTUzpbdKbgY';
bool downloading = false;
where uri is the URL for an Image online.
Inside initState() method, called the downloadFile() method, which is as below :
Future<void> downloadFile() async{
Dio dio= Dio();
try{
var dir= await getApplicationDocumentsDirectory();
await dio.download(uri, "${dir.path}/myimg.jpg", onReceiveProgress: (rec,total){
setState(() {
downloading=true;
});
});
}catch(e)
{
print("Error >> "+e.toString());
}
setState(() {
downloading=false;
print("Download Completes");
});
}
But, I am getting below error :
HTTP Status Error 403
MSG_WINDOW_FOCUS_CHANGED 1 1
D/InputMethodManager(15750): startInputInner - Id : 0
I/InputMethodManager(15750): startInputInner - mService.startInputOrWindowGainedFocus
D/InputMethodManager(15750): startInputInner - Id : 0
I/flutter (15750): DioError [DioErrorType.response]: Http status error [403]
I/flutter (15750): Source stack:
I/flutter (15750): #0 DioMixin.fetch (package:dio/src/dio_mixin.dart:488:35)
I/flutter (15750): #1 DioMixin.request (package:dio/src/dio_mixin.dart:483:12)
I/flutter (15750): #2 DioForNative.download (package:dio/src/entry/dio_for_native.dart:84:24)
I/flutter (15750): #3 _MyHomePageState.downloadFile (package:downloadfiles/main.dart:82:17)
I/flutter (15750):
I/flutter (15750): Download Completes
I am getting this error while running the app in android device, I have manually given File storage permission from settings for the application.
Also updated AndroidManifest.xml file as below :
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET"/>
Still am getting above error. What might causing the issue?
The HTTP 403 Forbidden response status code indicates that the server understands the request but refuses to authorize it.
This status is similar to 401, but for the 403 Forbidden status code, re-authenticating makes no difference. The access is permanently forbidden and tied to the application logic, such as insufficient rights to a resource.
String uri =
'https://i.picsum.photos/id/698/200/200.jpg?hmac=EElVlYPe8BAq1Btf4bWUxP9NoQP01_e8LTUzpbdKbgY';
This URL Asks for authorization in headers.
So use Authorization via headers for calling API.
Like this,
Future<void> downloadFile() async {
Dio dio = Dio();
try {
var dir = await getApplicationDocumentsDirectory();
await dio.download(uri,
options: Options(
headers: {"Authorization": "YOUR_AUTH_KEY HERE"},
),
"${dir.path}/myimg.jpg",
onReceiveProgress: (rec, total) {
setState(() {
downloading = true;
});
});
} catch (e) {
print("Error >> " + e.toString());
}
setState(() {
downloading = false;
print("Download Completes");
});
}
Related
I have been having a huge failure working with FormData using flutter. It works fine while I tested with postman which tells me the error is from my end. I need to post some list of files to the backend along with some parameters as such.
But it gives me 501 error each time which tells me that some data is invalid, however, there is no way to tell me exactly which parameter the error is located.
Dio dio = new Dio();
List<Map<String, int>> scheduled_slots = [
{"dateslot_id": 107, "timeslot_id": 97},
{"dateslot_id": 121, "timeslot_id": 120}
];
List<String> filesPath = ["/data/user/0/com.heckerbella.hope_clinic/app_flutter/1634521427103972"];
List<String> filesName = ["fileName"];
List<String> type =["Burning Pain", "Burning Pain"];
List<String> rating =["Just curious", "Mild Pain"];
FormData formData = FormData.fromMap({
"type": type,
"rating": rating,
"description": widget.description,
'files': [
await files.asMap().forEach((index, value) async {
await MultipartFile.fromFile
(filesPath[index], filename:
filesName[index]);
}),],
"scheduled_slots": scheduled_slots as String
});
try {
Response response = await dio.post(
Constants().API_BASE_URL +
"scheduled/slot/main/create/${selectedPlansData.id}",
data: formData,
options: Options(responseType: ResponseType.json, headers: {
"Authorization": "Bearer ${bloc.bearerToken}",
"Accept": "application/json",
}));
AlertManager.showToast(response.data.toString());
print(Constants().API_BASE_URL +
"scheduled/slot/main/create/${selectedPlansData.id}");
return response.data;
} catch (e) {
print(e.toString());
}
}
Error
I/flutter (10345): DioError [DioErrorType.response]: Http status error [501]
I/flutter (10345): #0 DioMixin.assureDioError (package:dio/src/dio_mixin.dart:819:20)
I/flutter (10345): #1 DioMixin._dispatchRequest (package:dio/src/dio_mixin.dart:678:13)
I/flutter (10345): <asynchronous suspension>
I/flutter (10345): #2 DioMixin.fetch.<anonymous closure>.<anonymous closure> (package:dio/src/dio_mixin.dart)
I/flutter (10345): <asynchronous suspension>
D/CompatibilityChangeReporter(10345): Compat change id reported: 147798919; UID 10146; state: ENABLED
E/Toast (10345): setGravity() shouldn't be called on text toasts, the values won't be used
You have issue with scheduled_slots, Dio only support fields on FormData is String, so when you pass a value is List<Map<String, int>>, Server will not receive it. Let deal with BE to convert scheduled_slots from List -> String.
I have been trying to launch a URL insder the flutter app. I am using url_launcher but am unable to launch the URL. The URL that I am receiving is a response from an API.
If I am copying the response and sending it statically, it's working, but when passing as a dynamic variable, it always shows "Could not be launched."
Code:
checkoutCartItem() async {
var url = Uri.parse(
'');
final headers = {'Content-Type': 'application/json'};
var body = jsonEncode({
"status": 2,
"uid": SignForm.userIdGlobal,
});
final encoding = Encoding.getByName('utf-8');
Response response = await post(
url,
headers: headers,
body: body,
encoding: encoding,
);
print(response.body);
Body.url = response.body.toString();
print(Body.url);
if (await canLaunch(Body.url.toString())) {
await launch(Body.url.toString());
} else {
throw 'Could not launch $Body.url';
}
}
Flutter version: 2.2,
url_launcher: ^6.0.4
Error:
[VERBOSE-2:ui_dart_state.cc(199)] Unhandled Exception: Could not launch [TextElement: '
', LinkElement: 'https://www.bsestarmf.in/ClientOrderPayment.aspx?K4HhW6zSxVp2T2sl9n5acA+J8qCjHcdVy2hyQmgbsuje2e6rf0+sujJisssdsaBFQV1zicfCer4VQUqJtRxgRiLYXwfXKkOBj9pA5dqrlOiLEPkxgWpB0QQa36DMiHhyqCA/fP60nFus9nGlM='
print(response.body)
Gives:
https://www.bsestarmf.in/ClientOrderPayment.aspx?K4HhW6zSxVp2T2sl9n5acA+J8qCjHcdVy2hyQmgbsuje2e6rf0+sujJiBFQV1zicfCer4VQUqJtRxgRiLYXwfXKkOBj9dddddpA5dqrlOiLEPkxgWpB0QQa36DMiHhyqCA/fP60nFus9nGlM=
[VERBOSE-2:ui_dart_state.cc(199)] Unhandled Exception: Could not launch Body.url
#0 checkoutCartItem (package:optymoney/Cart/Components/Body.dart:58:5)
<asynchronous suspension>
#1 _BodyState.build.<anonymous closure>.<anonymous closure> (package:optymoney/Cart/Components/Body.dart:326:29)
<asynchronous suspension>
This is a payment link that should open a browser inside the flutter app. But it's now working.
Please help!!!
N.B: Since this is a payment link, it has been edited and so it won't work if you try launching it from a browser
final payUrl = Uri.encodeFull(response.body);
if (await canLaunch(payUrl)) {
await launch(payUrl, forceWebView: true, enableJavaScript: true);
} else {
throw 'Could not launch $payUrl';
}
This is how I resolved the issues. Turns out that encoding the received response was the solution needed.
I am trying to upload a jpg image using Form data through the Dio package, the request is working properly on Postman, so i think the problem is either in my code or in the image am trying to upload.
As for the image, I have a bitmap with some edits applied on it, i turn this btm into a Uint8List and then I encode it into a jpg file, i use the Bitmap plugin to do that.
final directory = await getApplicationDocumentsDirectory();
File image = await File('${directory.path}/image.jpg').create();
await image.writeAsBytes(widget.editedBitmap.buildHeaded());
Then i get the image width and height through decoding it
var decodedImage = await decodeImageFromList(imageBytes);
print(decodedImage.width);
print(decodedImage.height);
and then i create my form data
FormData formData = new FormData.fromMap({
'title': titleController.text,
'description': descriptionController.text,
'is_public': privacy == 'Public' ? true : false,
'photo_width': decodedImage.width,
'photo_height': decodedImage.height,
'media_file': await MultipartFile.fromFile(
image.path,
filename: image.path.split("/").last,
contentType: new MediaType("image", "jpeg"),
),
});
then i create my dio instance and configure the options
var dio = new Dio();
dio.options.baseUrl = globals.HttpSingleton().getBaseUrl();
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout = 3000;
dio.options.headers = {
HttpHeaders.acceptHeader: '*/*',
HttpHeaders.authorizationHeader: 'Bearer ' + globals.accessToken,
HttpHeaders.contentTypeHeader: 'multipart/form-data'
};
and here is my request
Response response;
try {
response = await dio.post(
'/photos/upload',
data: formData,
onSendProgress: (int sent, int total) {
print('$sent $total');
},
);
} on DioError catch (e) {
print(e.response.data);
}
And here is the response (e) i get
I/flutter (12154): <html>
I/flutter (12154): <head><title>413 Request Entity Too Large</title></head>
I/flutter (12154): <body>
I/flutter (12154): <center><h1>413 Request Entity Too Large</h1></center>
I/flutter (12154): <hr><center>nginx/1.18.0 (Ubuntu)</center>
I/flutter (12154): </body>
I/flutter (12154): </html>
when I searched about this, it said that 413 happens when my request is too large for the server, however, using postman, and while uploading a bigger sized image, it still works which doesn't make sense to me, any idea what could be the problem?
If any further info is needed please let me know.
Turns out when the server was deployed, if not specified, the upload limit is set to 1MB by default, and due to my mistake of uploading different images with different sizes from phone and postman, it took me a lot of time to figure it out. I contacted the DevOps and it was solved.
I have a Dio client with an interceptor attached, but when I try to add some headers, they don't appear in logs.
his is my code
Dio dio(KeyStore keyStore) {
final Dio dio = Dio(BaseOptions(
contentType: 'application/json',
connectTimeout: 5000,
sendTimeout: 5000,
));
final Dio tokenDio = Dio();
tokenDio.interceptors.add(LogInterceptor(responseBody: true));
dio.interceptors.add(LogInterceptor(responseBody: true));
dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) async {
final accessToken = await keyStore.readAccessToken();
final branchId = keyStore.readBranchId();
dio.lock();
options.headers[HttpHeaders.authorizationHeader] = 'Bearer: $accessToken';
options.headers['x-tenant'] = branchId;
handler.next(options);
dio.unlock();
}));
return dio;
}
Request headers...
I/flutter ( 8872): *** Request ***
I/flutter ( 8872): uri: https://api.someapi.com/users/058c026a-2dce-47e7-9fe1-61dec507265f
I/flutter ( 8872): method: GET
I/flutter ( 8872): responseType: ResponseType.json
I/flutter ( 8872): followRedirects: true
I/flutter ( 8872): connectTimeout: 5000
I/flutter ( 8872): sendTimeout: 5000
I/flutter ( 8872): receiveTimeout: 0
I/flutter ( 8872): receiveDataWhenStatusError: true
I/flutter ( 8872): extra: {}
I/flutter ( 8872): headers:
I/flutter ( 8872):
Dio interceptors are run in order. Since your auth interceptor is added after the log interceptor, LogInterceptor is called first, before the auth interceptor has a chance to add headers. The interceptor code looks fine to me, so I suspect that the authorization headers are correctly sent to the remote server.
To make your logs reflect the headers added by the auth interceptor, make sure LogInterceptor is the last element of the list dio.interceptors.
You don't necessarily need to use an interceptor.
You can update the default options like this:
dio.options.headers = {
HttpHeaders.authorizationHeader: 'Bearer: $accessToken',
'x-tenant': branchId
}
Then subsequent requests will use these values by default.
I have tried uploading image using formData which Dio plugin supports.
FormData formData = new FormData.from(
{"profile_image": UploadFileInfo(image, "profile_image.jpg")});
var response = await _dio.post(ApiConfiguration.getUploadImageUrl().toString(),data: formData);
But its returning error.
DioError [DioErrorType.DEFAULT]: SocketException: OS Error: Connection reset by peer, errno = 54, address = 3.122.199.93, port = 62181
Any help would be appreciated.
UploadFileInfo and FormData.from deprecated in v3. Documentation needs to be clearer and updated. I wasted a good part of a day with this.
I used dio for post a file path with some other information in this way :
Dio dio = new Dio();
FormData formData = new FormData();
formData.add(
"apiKey",
"my_api_key",
);
formData.add(
"file",
"image_path",
);
Response response = await dio.post(
"https://localhost",
data: formData,
onSendProgress: (int sent, int total) {
// do something
},
).catchError((onError) {
throw Exception('something');
});
Faced something similar in Release version of the app, Dio was failing to upload images. This could be due to two reasons, one is mentioned above that you are using a deprecated methods in v3 or You can try adding this permission to the Manifest
Add this permission
<uses-permission android:name="android.permission.INTERNET" />
to your app level Manifest.
android/app/src/main/AndroidManifest.xml
Solution can be found here
https://stackoverflow.com/a/59392036/14641365
Although the other work around to upload an image to the server that I used is something as follows
String url = "YOUR_URL_TO_UPLOAD";
FormData formData = FormData.fromMap({
"QUERY_PARAM_NAME": await MultipartFile.fromFile("IMAGE_PATH", filename:"TESTING.jpg"),
});
var response = await dio.post(url, data: formData);