I am tring to develop a app using Flutter Web to provide a gallery of mediafiles stored in an apache server. Currently I am using Dio to get files as bytes and then call saveAs javascript function to downloand files to client computer. While this approach works for small sized files, it gives out of memory error for big files in browser. My code to get files is as follows. Can you suggest me a better way to get files? Thanks.
response = await dio.get(
medialink,
cancelToken: cancelToken,
onReceiveProgress: (rcv, total) {
if (total != -1) {
setState(() {
progress = ((rcv / total) * 100).toStringAsFixed(0);
});
if (rcv == total) {
setState(() {
isDownloaded = true;
downloading = false;
});
}
}
else {
print("-1");
}
},
//Received data with List<int>
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
validateStatus: (status) { return status! < 500; }
),
);
Related
I am using the POST method to upload video file from Flutter App using the Dio package. Vimeo's POST method first generates a link for us to upload the video to. The video is uploaded successfully most of the time. Every once in a while, it fails (like 1/10).
To Test if this is an issue on Vimeo's server I tried uploading the video from POSTMAN, 20/20 (all of them) times it uploaded successfully. So I am assuming there is something wrong with the DIO Package?
I have also posted this issue in the DIO's github issues:
https://github.com/flutterchina/dio/issues/1535#issue-1328014439
Upload video code:
if (url != null) {
print('upload link is not null!');
Strings.videoURL = url;
print('upload url is <>:');
print(Strings.videoURL);
try {
Dio uploadVideoDio = new Dio();
uploadVideoDio.options.headers["authorization"] = dotenv.env['VIMEO_ACCESS_TOKEN'];
uploadVideoDio.options.headers["content-type"] = "application/json";
uploadVideoDio.options.headers["accept"] = "application/json";
var formData = FormData.fromMap({
"file_data": await MultipartFile.fromFile(file.path)
});
await uploadVideoDio.post(
Strings.videoURL,
data: formData,
options: Options(
followRedirects: false,
responseType: ResponseType.json,
validateStatus: (status) {
print('status is :');
print(status);
// return status! < 500;
if (status == 302) {
uploadStatus = 'successful';
return true;
} else {
return false;
}
}),
);
print('file upload called');
print('upload status is ');
print(uploadStatus);
return uploadStatus;
} on DioError catch (err) {
print('error uploading actual video');
print(err.toString());
return uploadStatus;
}
} else {
print('upload link is null');
return uploadStatus;
}
Here's an update
I got in touch with the Vimeo's support team and they are investigating the issue. Their response is that
The "POST" method is not very reliable and you should use the TUS
resumable approach.
I am about to test the stability of the TUS method and will update this post afterwards
In my app I am downloading a files from server. I need to save this files to downloads folder in mobile phone storage. Can this was possible using path_provider package in android ?
This might be a duplicate of this question.
Checkout this answer.
You might want to consider saving the files in your app directory of your app, as described in the official pub.dev docs of path_provider.
You can use Dio for downloading and downloads_path_provider_28 for getting download folder path collectively for this:
Future download(String url) async {
final Dio dio = Dio();
Directory? downloadsDirectory = await DownloadsPathProvider.downloadsDirectory; // "/storage/emulated/0/Download"
final savePath = downloadsDirectory?.path;
try {
Response response = await dio.get(
url,
onReceiveProgress: (received, total) {
if (total != -1) {
print((received / total * 100).toStringAsFixed(0) + "%");
}
},
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
validateStatus: (status) {
return status < 500;
}
),
);
print(response.headers);
File file = File(savePath);
var raf = file.openSync(mode: FileMode.write);
// response.data is List<int> type
raf.writeFromSync(response.data);
await raf.close();
} catch (e) {
print(e);
}
}
I am stuck in issue with upload large file. A flutter application compiled for Web and App (Native) when file upload from app is working fine but web it hanged. how to send large file as stream request.
I am new in flutter and working on current existing app which has a feature to upload large tutorial video files and PDF files. requirement is to show the progress bar during the upload the file, currently app has I used dio but it hanged in web version and not file not upload operation going failed.
File size approximate 400MB to 700MB
Currently using following packages
dependencies:
http: ^0.12.2
dio: ^3.0.10
Could you please help me out for this issue?
I am trying to achieve with below code but some how it not working.
https://github.com/salk52/Flutter-File-Upload-Download
It's thrown an error like "Memory buffer allocation failed ", I am unable to update the version of dio or http due to other package goes disturb. somehow I have to achieve it using httpclient or dio. I could not update the version for package because it messed up other package dependency.
Sample ref. code as below:
File size approximate around 500 MB to 700MB
For ref. following code which using in code.
Dio package example:
#Dio example start
Future<NormalResponse> addSubjectMaterial(
{GetSubjectMaterial objSubMat,
bool isDelete,
PlatformFile objfile,
Function sendProgress,
Function receiveProgress,
dio.CancelToken cancelToken}) async {
NormalResponse objRes = NormalResponse();
try {
print(objSubMat.objMaterial.subjectId);
dio.FormData formData = dio.FormData();
formData.fields.add(MapEntry("ObjSubMat", json.encode(objSubMat)));
formData.fields.add(MapEntry("IsDelete", isDelete.toString()));
formData.fields
.add(MapEntry("ClassesId", AppConstants.classesId().toString()));
if (objfile != null) {
formData.files.add(
MapEntry("objfile", await getMultipartFile(objfile, "objfile")));
}
var resp = await dio.Dio().post(
AppConstants.addUpdateSubjectMaterial,
data: formData,
options: requestConfig,
cancelToken: cancelToken,
onSendProgress: sendProgress,
onReceiveProgress: receiveProgress,
);
// String respStr = resp.toString();
// objRes = NormalResponse.fromJson(json.decode(respStr));
objRes = NormalResponse.fromJson(resp.data);
} catch (err) {
objRes.err = err.toString();
objRes.isSuccess = false;
objRes.newId = -1;
sendProgress = null;
receiveProgress = null;
}
return objRes;
}
#Dio example end
#httpclient example code is there any solution with progress bar in this sample code.
Future<NormalResponse> addUpdateSubjectMaterialHttp(
{GetSubjectMaterial objSubMat,
bool isDelete,
PlatformFile objfile,
Function sendProgress,
Function receiveProgress,
dio.CancelToken cancelToken}) async {
NormalResponse objRes = NormalResponse();
try {
var req = http.MultipartRequest(
"POST",
Uri.parse(AppConstants.addUpdateSubjectMaterial),
);
req.headers.addAll({
'Content-type': 'application/json',
'Accept': 'application/json',
});
req.fields['ObjSubMat'] = json.encode(objSubMat);
req.fields['IsDelete'] = isDelete.toString();
req.fields['ClassesId'] = AppConstants.classesId().toString();
if (objfile != null) {
req.files.add(http.MultipartFile(
"objFile", objfile.readStream, objfile.size,
filename: objfile.name));
}
var resp = await req.send();
String result = await resp.stream.bytesToString();
objRes = NormalResponse.fromJson(json.decode(result));
print(objRes.isSuccess);
print(objRes.err);
print("Here done");
} catch (err) {
print(err);
throw err;
}
return objRes;
}
#httpclient
Http package example:
#example start
Future<NormalResponse> addSubjectMaterial(
{GetSubjectMaterial objSubMat,
bool isDelete,
PlatformFile objfile,
Function sendProgress,
Function receiveProgress,
dio.CancelToken cancelToken}) async {
NormalResponse objRes = NormalResponse();
try {
print(objSubMat.objMaterial.subjectId);
dio.FormData formData = dio.FormData();
formData.fields.add(MapEntry("ObjSubMat", json.encode(objSubMat)));
formData.fields.add(MapEntry("IsDelete", isDelete.toString()));
formData.fields
.add(MapEntry("ClassesId", AppConstants.classesId().toString()));
if (objfile != null) {
formData.files.add(
MapEntry("objfile", await getMultipartFile(objfile, "objfile")));
}
var resp = await dio.Dio().post(
AppConstants.addUpdateSubjectMaterial,
data: formData,
options: requestConfig,
cancelToken: cancelToken,
onSendProgress: sendProgress,
onReceiveProgress: receiveProgress,
);
// String respStr = resp.toString();
// objRes = NormalResponse.fromJson(json.decode(respStr));
objRes = NormalResponse.fromJson(resp.data);
} catch (err) {
objRes.err = err.toString();
objRes.isSuccess = false;
objRes.newId = -1;
sendProgress = null;
receiveProgress = null;
}
return objRes;
}
#example end
You must use Future, await and async which continues the task of streams in background thread while the UI of your application works smoothly.
I have been working for few since yesterday to try upload an image to azure blob storage taken using mobile camera form iOS/Android device.
I am able to upload the files but for some reason they being corrupted not able to open the image uploaded.
Please check the image error while opening the uploaded image
I am using flutter package http with different approach all work in uploading image file to azure blob store but it gets corrupted somehow , I tried forcing the ContentType to image/jpeg but no help.
Here is code I am using an http API -
takePicture() async {
final pickedFile = await picker.getImage(source: ImageSource.camera);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
String fileName = basename(pickedFile.path);
uploadFile(fileName, image);
} else {
print('No image selected.');
}
});
}
First approach -->
http.Response response = await http.put(
uri,
headers: {
"Content-Type": 'image/jpeg',
"X-MS-BLOB-TYPE": "BlockBlob",
},
body: image.path,
);
print(response.statusCode);
Using Approach second -->
final data = image.readAsBytesSync();
var dio = Dio();
dio.options.headers['x-ms-blob-type'] = 'BlockBlob';
dio.options.headers['Content-Type'] = 'image/jpeg';
try {
final response = await dio.put(
'$url/$fileName?$token',
data: data,
onSendProgress: (int sent, int total) {
if (total != -1) {
print((sent / total * 100).toStringAsFixed(0) + "%");
}
},
);
print(response.statusCode);
} catch (e) {
print(e);
}
Approach third -->
var request = new http.MultipartRequest("PUT", postUri);
request.headers['X-MS-BLOB-TYPE'] = 'BlockBlob';
request.headers['Content-Type'] = 'image/jpeg';
request.files.add(
new http.MultipartFile.fromBytes(
'picture',
await image.readAsBytes(),
),
);
request.send().then((response) {
uploadResponse.add(response.statusCode);
}, onError: (err) {
print(err);
});
Help here is much appreciated.
If you want to upload the image to Azure Blob Storage in the flutter application, you can use the Dart Package azblob to implement it. Regarding how to use the package, please refer to here.
For example
import 'package:image_picker/image_picker.dart';
import 'package:flutter/material.dart';
import 'package:azblob/azblob.dart';
import 'package:mime/mime.dart';
...
//use image_picker to get image
Future uploadImageToAzure(BuildContext context) async {
try{
String fileName = basename(_imageFile.path);
// read file as Uint8List
Uint8List content = await _imageFile.readAsBytes();
var storage = AzureStorage.parse('<storage account connection string>');
String container="image";
// get the mine type of the file
String contentType= lookupMimeType(fileName);
await storage.putBlob('/$container/$fileName',bodyBytes: content,contentType: contentType,type: BlobType.BlockBlob);
print("done");
} on AzureStorageException catch(ex){
print(ex.message);
}catch(err){
print(err);
}
Unfortunately, the multipart form is causing break of image. I don't know how it works on azure side, because there is little or no information about multipart uploads, but it's clearly broken because of multipart form. I replicated the problem in .net core application and whenever i am using multipart form data to upload image - it is broken. When i am using simple ByteArrayContent - it works. I couldn't find flutter equivalent to ByteArrayContent, so i am lost now :( The package mentioned by #Jim is useless for me, because i want to give clients sas url, so they have permission to upload image on client side. I do not want to store azure storage account secrets in flutter app.
EDIT. I found the solution to send raw byte data with Dio package. You can do that also with http package.
final dio = new Dio();
final fileBytes = file.readAsBytesSync();
var streamData = Stream.fromIterable(fileBytes.map((e) => [e]));
await dio.put(uploadDestinationUrl,
data: streamData,
options: Options(headers: {
Headers.contentLengthHeader: fileBytes.length,
"x-ms-blob-type": "BlockBlob",
"content-type": "image/jpeg"
}));
Requirement:
I've 1000 URLs of PDF, from which I want to create the zip.
Function:
static Future<bool> downloadZip(List<String> urls) {
int counter = 0
for(String url in urls) {
bytesForFileAtUrl(url).then((bytes){
counter++;
if(bytes != null) {
//Add bytes to the zip encoder
}
if(counter == urls.length) {
//close the zip encoder
//download the zip file
}
}
}
}
static Future<Uint8List> bytesForFileAtUrl(String url) async {
try {
http.Response response = await http.get(url);
return response?.bodyBytes;
} catch (e) {
print('Error getting bytes: ${e.toString()}');
return null;
}
}
Problem:
When there is small number of requests its working fine. But in case large number of requests I'm getting the exception: SocketException: Connection failed (OS Error: Too many open files, errno = 24) as reported here. This might because of memory issue on my mobile device, as its working fine on the web platform.
I've tried other solutions like the one using Future.wait() suggested here, but its not working and device is hanged.
Whats the best solution for this scenario ?
Worst Solution:
Using await keyword. No exception at all, but then its taking too long for the overall process to complete.
for(String url in urls) {
final bytes = await bytesForFileAtUrl(url);
...
...
}
static Future < bool > downloadZip(List < String > urls) {
int counter = 0;
urls.forEach(async() {
await bytesForFileAtUrl(url).then((bytes){
counter++;
if (bytes != null) {
//Add bytes to the zip encoder
}
if (counter == urls.length) {
//close the zip encoder
//download the zip file
}
});
});
}
static Future < Uint8List > bytesForFileAtUrl(String url) async {
try {
http.Response response = await http.get(url);
return response?.bodyBytes;
} catch (e) {
print('Error getting bytes: ${e.toString()}');
return null;
}
}
maybe try this ?