781flyingdutchman / background_downloader Goto Github PK
View Code? Open in Web Editor NEWFlutter plugin for file downloads and uploads
License: Other
Flutter plugin for file downloads and uploads
License: Other
First of all, thanks for the nice new plugin which i happily replaced the outdated flutter_downloader with!
An issue came up:
final task = DownloadTask(url: finalUrl, filename: fileName);
var downloadResult = await FileDownloader().download(task);
if (downloadResult == TaskStatus.complete) {
final newFilePath = await FileDownloader().moveToSharedStorage(task, SharedStorage.downloads);
if (newFilePath == null) {
ERROR Msg
} else {
FileService().openFile(newFilePath);
}
}
Moving the file to the shared storage works fine on Android and my iOS simulator, but fails on some customer iPhones, returning null.
Feels like some permission issue..
Any idea what I could be missing? Thanks!
ad:
Or is there an option to open a file (e.g. pdf) direct after download from the app's document folder? I guess, i have to move it to the shared storage to allow an associated pdf viewer app to open it..
I still want to have the download progress on Android and iOS. On Windows you could also use the taskbar.
That should be optional of cause but for my usecase I would need it. I know that here kicks in localization problems, but I guess that would be not the frist plugin which runs into that issue.
First off, many thanks for this library @781flyingdutchman , I am really loving it so far! It's very convenient to use, much simpler than flutter_downloader ever was!
I get the following NullPointerException from time to time when calling await FileDownloader().allTasks()
in my taskStatusCallback
method of the FileDownloader().registerCallbacks()
method. It seems to stem from the "native_downloader.dart" file in the library (see screenshot).
Am I doing something wrong by calling allTasks() in the callback? Or is there a small bug in the library itself?
The full error message is this:
java.lang.NullPointerException: null cannot be cast to non-null type kotlin.String
at com.bbflight.background_downloader.BackgroundDownloaderPlugin.methodAllTasks(BackgroundDownloaderPlugin.kt:380)
at com.bbflight.background_downloader.BackgroundDownloaderPlugin.access$methodAllTasks(BackgroundDownloaderPlugin.kt:51)
at com.bbflight.background_downloader.BackgroundDownloaderPlugin$onMethodCall$1.invokeSuspend(BackgroundDownloaderPlugin.kt:291)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:284)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:1)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source:1)
at com.bbflight.background_downloader.BackgroundDownloaderPlugin.onMethodCall(BackgroundDownloaderPlugin.kt:287)
at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:258)
at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322)
at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8128)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:946)
This leads me to a feature suggestion:
Basically what I am using allTasks()
for is to find out if any downloads are still in progress and change the UI accordingly. It's not that hard to do, I just check if the returned List is empty or not, but I would appreciate a "isDownloadInProgress" call, or something along the lines.
Because every version of flutter_test from sdk depends on collection 1.16.0 and background_downloader >=5.0.0 depends on collection ^1.17.0, flutter_test from sdk is incompatible with background_downloader >=5.0.0.
So, because vectortiles_to_rastertiles depends on both background_downloader ^5.2.0 and flutter_test from sdk, version solving failed.
pub get failed (1; So, because vectortiles_to_rastertiles depends on both background_downloader ^5.2.0 and flutter_test from sdk, version solving failed.)
exit code 1
How i change or increase flutter_test dependency.
Hello,
I've tried to listen to the updates
stream, but as it is a normal stream, I can listen it to only once. I mean, if I called listen()
once and after cancelled the subscribtion, then I can't call listen
again on it, cause dart throws bad state error (streams can be only listened once). The use case is this: there is a screen where I start to listen to the stream, then navigate away, then back and I receive the error, because it was listened to on the first navigation. If the updates
stream was a broadcast stream, that would solve this problem, but then it would lose the buffering feature (by default) which I don't know how important here. (Edit: Just to make sure: I don't want/trying to listen to the stream multiple times at once).
The second option is registerCallbacks()
which would be a good alternative (I prefer stream though), but how do I unregister them? Only destroy
clears them in base_downloader.dart
as far as I see 🤔 I would like to unregister them when I navigate away.
Edit2: Maybe calling destroy
would be the proper way when navigating away? As I see now it recreates the stream controller. Will try it later.
Hi I revisited my application after a while (last used version 1.6.0) and I encountered an error which I did not recognise happened ever before. Somehow it's crashing the app on both package versions.
The code used to download the content:
await FileDownloader().download(DownloadTask(
url: contentUrl,
filename: fileName,
baseDirectory: BaseDirectory.applicationDocuments,
directory: 'courses/${course.courseId}/${step.stepId}',
));
Migrated from await FileDownloader.download(BackgroundDownloadTask)
.
The error that appears in the console:
[Downloader] Starting task with id 158596712
[Downloader] Starting task with id 635268047
[Downloader] Fnished task with id 158596712
[Downloader] Starting task with id 1908250544
2
background_downloader/Downloader.swift:124: Fatal error: Unexpectedly found nil while unwrapping an Optional value
* thread #1, queue = 'com.apple.main-thread', stop reason = Fatal error: Unexpectedly found nil while unwrapping an Optional value
frame #0: 0x00000001c3dad0f8 libswiftCore.dylib`_swift_runtime_on_report
libswiftCore.dylib`:
-> 0x1c3dad0f8 <+0>: ret
libswiftCore.dylib`:
0x1c3dad0fc <+0>: b 0x1c3dad0f8 ; _swift_runtime_on_report
libswiftCore.dylib`:
0x1c3dad100 <+0>: adrp x8, 365860
0x1c3dad104 <+4>: ldrb w0, [x8, #0xabc]
Target 0: (Runner) stopped.
Lost connection to device.
This was taken from launch on physical device, but simulator is crashing as well. I've tried this in both release and debug same results. I'm willing to continue providing more detailed information about content and app via pm, can't do it directly here from obvious reasons.
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:prime_layer_flutter/utils/common.dart';
import 'package:sn_progress_dialog/sn_progress_dialog.dart';
import 'package:background_downloader/background_downloader.dart';
import 'package:flutter_gen/gen_l10n/century_localizations.dart';
InAppWebViewSettings options = InAppWebViewSettings(
mediaPlaybackRequiresUserGesture: false,
// disallowOverScroll: true,
alwaysBounceHorizontal: false,
alwaysBounceVertical: false,
useOnLoadResource: true,
// clearCache: true,
);
class Browser extends StatefulWidget {
const Browser(this.url, this.title, {super.key});
final String url;
final String title;
@override
BrowserState createState() => BrowserState();
}
class BrowserState extends State<Browser> {
final GlobalKey webViewKey = GlobalKey();
bool _isLoading = true;
double? _htmlHeight;
ProgressDialog? pd;
double downloadProgress = 0;
String currentTaskId = '';
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
InAppWebViewController webViewController;
final localizations = AppLocalizations.of(context)!;
// pd = ProgressDialog(context: context);
return SafeArea(
child: Stack(
children: [
SizedBox(
height: MediaQuery.of(context).size.height,
child: InAppWebView(
// gestureRecognizers: gestureRecognizers,
onDownloadStartRequest: (controller, downloadStartRequest) async {
final task = DownloadTask(
url: downloadStartRequest.url.toString(),
filename: downloadStartRequest
.url.queryParameters['fullFileName'] ??
'附件'); // define your task
pd ??= ProgressDialog(context: context);
pd!.show(
max: 100,
msg: localizations.downloadStatusRunning,
progressType: ProgressType.valuable,
progressBgColor: Colors.transparent,
hideValue: true,
closeWithDelay: 1000,
cancel: Cancel(
cancelClicked: () {
FileDownloader().cancelTaskWithId(task.taskId);
},
),
completed: Completed(
completedMsg: localizations.downloadStatusComplete),
// onStatusChanged:
);
FileDownloader().download(
task,
onProgress: (progress) {
// setState(() {
// downloadProgress = progress;
// });
print('---Progress update: $progress');
pd!.update(
value: progress.toInt(),
);
if (progress == 1) {
pd!.close();
FileDownloader().openFile(task: task);
}
},
onStatus: (status) => print('Status update: $status'),
);
},
// onLoadResource: (controller, resource) {
// },
key: webViewKey,
initialUrlRequest: URLRequest(url: WebUri(widget.url)),
initialSettings: options,
onWebViewCreated: (InAppWebViewController controller) {
setState(() {
_isLoading = true;
});
webViewController = controller;
webViewController.addJavaScriptHandler(
handlerName: 'goback',
callback: (args) {
Navigator.of(context).pop();
},
);
webViewController.addJavaScriptHandler(
handlerName: 'backToWorkBench',
callback: (args) {
Navigator.of(context).pop();
},
);
webViewController.addJavaScriptHandler(
handlerName: 'getToken',
callback: (_) async {
return await CommonUtils().getToken();
},
);
},
onLoadStop: (InAppWebViewController controller, _) async {
int contentHeight = (await controller.getContentHeight())!;
double zoomScale = (await controller.getZoomScale())!;
double? htmlHeight = contentHeight.toDouble() * zoomScale;
double htmlHeightFixed =
double.parse(htmlHeight.toStringAsFixed(2));
if (htmlHeightFixed == 0.0) {
return;
}
setState(() {
_htmlHeight = htmlHeightFixed + 0.1;
_isLoading = false;
});
},
),
),
Offstage(
offstage: !_isLoading,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white,
child: const Center(
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
),
)
],
),
);
}
}
class ItemHolder {
ItemHolder({this.name, this.task});
final String? name;
final TaskInfo? task;
}
class TaskInfo {
TaskInfo({this.name, this.link});
final String? name;
final String? link;
String? taskId;
int? progress = 0;
}
Hey
thanks for the awesome plugin!
Everything works flawlessly on ios. On android on the other hand the downloadStatusCallback
or downloadProgressCallback
is not being called after at all. The native logs appear in the logcat indicating a successful download.
During my small debugging, everything seemed to work on the native side, but in the flutter the _backgroundChannel
MethodCallHandler
never gets called.
Thanks in advance
FAILURE: Build failed with an exception.
A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations$ReflectSdkVersion found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.jvm.jdk8.JvmRepeatableKt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.jvm.optionals.OptionalsKt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.random.jdk8.PlatformThreadLocalRandom found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.streams.jdk8.StreamsKt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$1 found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$2 found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$3 found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$4 found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.text.jdk8.RegexExtensionsJDK8Kt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Duplicate class kotlin.time.jdk8.DurationConversionsJDK8Kt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.7.20 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20)
Go to the documentation to learn how to Fix dependency resolution errors.
Run with --stacktrace option to get the stack trace.
Run with --info or --debug option to get more log output.
Run with --scan to get full insights.
BUILD FAILED in 1m 13s
Exception: Gradle task assembleLaunchDebug failed with exit code 1
Exited (sigterm)
baseDirectory: BaseDirectory.applicationDocuments
is invisible to the user on Android unfortunately and we can't force an absolute directory in the arguments directory
I'm trying to understand how you build your library. I noticed that you are using httpConnection.contentLengthLong
in DownloadWorker:260, this requires API level 24, but you can replace it with this one liner:
val contentLength = httpConnection.getHeaderField("content-length").toLongOrNull() ?: -1
You should also limit your response code handling to 200 to download the file. A 206 for "Partial Content" is currently not supported by your code.
Hi,
I'm using await FileDownloader().downloadBatch(...)
and I couldn't find a way to disable notification (for a specific batch download or in general).
Is it possible? I'm on background_downloader: ^5.4.5
Thanks :)
Hello,
I have tried out your package and it seems to be very superior to the flutter_downloader, so thank you!
But when playing with pause/resume I'm struggling a bit. I cannot resume a DownloadTask
which has allowPause: true
and FileDownloader().taskCanResume(task)
also says its resumable. Well, it is resumed, just the original onStatus
and onProgress
callbacks are not triggered anymore. Is it intended? As I see I cannot reatach callbacks when resuming.
I tried this only on android (10 on a real device, 12-13 in emulator).
Steps:
flutter create projname
. I'm using 3.7.12
(most recent stable).lib/main.dart
's content by this gist: https://gist.github.com/slaci/ec1bae0a6648a84848f5d16e477eb540minSdkVersion
to 24 in android/app/build.gradle
ext.kotlin_version
to 1.8.20
in android/build.gradle
(stable flutter uses only 1.7)DownloadBtn(fileUrl: 'https://...')
.resume
returns true
, but status and progress won't update anymore. Download will eventually complete as you can see in the logs and the file will be there.I am using this library to download small video files (120 videos in total, size ranging between 19-50 MBs). I want users to be able to download them individually or, if they choose to do so, all at once.
Whenever I try to download all videos at once on my test device on Android, all tasks do get enqueued, but they never start downloading or finish, not even one of them. Also, the tasks that are enqueued aren't cancelled unless I trigger a cancel via a button.
If I trigger a download of a smaller number of files, say 20, they do all get downloaded completely.
I created a very similar app about 2 years ago with the flutter_downloader
package with no issues, so I doubt that this is an operating system issue (the app still works today on my phone).
Can you help me out? Is this something this library isn't intended to do, am I doing something wrong or is there a bug?
There is an example in the example app, but it may be helpful to add a widget to the package itself that developers can use to show download progress in a standardized way, as it's a common use case. Should not rely on Provider or similar package (as the example does) to reduce dependency conflict risk.
In the case of background download, there are many downloads of large files.
Many Android users often expand their capacity with a microSD card.
I want to set the download path to the micro sd card path, but there is no such method in the current plug-in.
You can think of moving files after downloading by implementing them separately, but since MircroSD Card is a separate physical storage device, the cost is higher than moving files internally.
Hello,
I just tried out this new withSuggestedFilename()
function, but realised my server returns the mentioned (and all other) header(s) in lowercase. As the dart code checks only for Content-Disposition
, it does not detect the header.
I would like to pause a running task and then continue downloading, or delete a task completely, Can this plugin be implemented? thank you
Hello,
When writing tests which uses Filedownloader I always want to use mocks instead of the real class, as I don't want to download anything. But now on 7.0+ you marked many classes with final and mockito cannot generate mocks from them anymore sadly:
Error: The class 'FileDownloader' can't be implemented outside of its library because it's a final class.
class MockFileDownloader extends _i1.Mock implements _i14.FileDownloader
Are you sure those finals are really required?
Build runner complaining for these currently:
Database
TaskStatusUpdate
Batch
I created an issue for mockito about this: dart-lang/mockito#635
Perhaps I need to adjust my implementation.
I have multiple widgets which I wish to observe download status/progress.
One such example is comparing the task.taskId
and updating the widget state:
@override
void initState() {
super.initState();
FileDownloader.registerCallbacks(downloadStatusCallback: (task, status) {
if (task.taskId == _taskId) {
setState(() {
_downloadStatus = status;
});
}
}, downloadProgressCallback: (task, progress) {
if (task.taskId == _taskId) {
setState(() {
_progress = progress;
});
}
});
}
Hi, I tried using the package on a child isolate and got an error because platform channels cannot be used on child isolates. After doing some research I came across this article Background Isolate channels so it seems with Flutter 3.7 it is possible to use platform channel methods on other isolates and tried doing the same thing as in the sample code.
void _isolateMain(RootIsolateToken rootIsolateToken) async {
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
FileDownloader();
}
but received an error at runtime.
[VERBOSE-2:dart_isolate.cc(1098)] Unhandled exception:
UI actions are only available on root isolate.
#0 FfiTrampoline____nativeSetNeedsReportTimings$Method$FfiNative$Ptr (dart:ffi)
#1 PlatformDispatcher.__nativeSetNeedsReportTimings (dart:ui/platform_dispatcher.dart:524:24)
#2 PlatformDispatcher._nativeSetNeedsReportTimings (dart:ui/platform_dispatcher.dart:521:52)
#3 PlatformDispatcher.onReportTimings= (dart:ui/platform_dispatcher.dart:513:29)
#4 SchedulerBinding.addTimingsCallback
binding.dart:308
#5 SchedulerBinding.initInstances
binding.dart:240
#6 ServicesBinding.initInstances
binding.dart:37
#7 PaintingBinding.initInstances
binding.dart:20
#8 SemanticsBinding.initInstances
binding.dart:18
#9 RendererBinding.initInstances
binding.dart:30
#10 <…>
maybe I'm missing something ?
hello:
Now. The background_downloader resume download file when call resume method.
The file not download completed when user exit the app. so I need continue download when start the app.
What should I do?
I'm using this package to download mp3 files. In iphone 12 (iOS version 16.5) everything is working perfect. But no notification is showing when I'm testing in iphone X (iOs version: 16.5) and iphone 7 (iOS version: 15.7). But task is downloading in directory. Foloowing is the code.
final task = bd.DownloadTask(
url: url,
filename: '$name.mp3',
); // define your task
bd.FileDownloader().configureNotification(
running: bd.TaskNotification('Downloading', 'file: "$name.mp3"'),
progressBar: true,
tapOpensFile: true,
complete: bd.TaskNotification('Download Complete', 'file: $name.mp3'),
error: bd.TaskNotification('Download Failed', 'file: $name.mp3'),
);
final result = await bd.FileDownloader()
.download(task);
print(result.status);
Hello dear!
I want to download file from a server but before start downloading, show an exception for me...
Chain validation failed javax.net.ssl.SSLHandshakeException: Chain validation failed Chain
my SSL is expired but i don't want to occur any error for users.
How can I activate "SSL Ignore"?
Thanks.
According to the documentation, redirects should be followed by the downloader, but when I try to download a file from a URL which redirects to a S3 bucket address, I get the following exception:
I/TaskWorker( 8604): Response code 302 for download from <url>
I/TaskWorker( 8604): Could not read response content from httpResponseCode 302: java.lang.NullPointerException: connection.errorStream must not be null
Using version 6.1.2 on Android with flutter 3.7.3 and Dart 2.
I am planning to add this support to my 2.0 fork, but why not also in your plugin. I'm just thinking if we could combine our resources. I wrote such code already in the past and for Android 14 I need to maintain it anyway.
I still need to evaluate your interface to understand better how you do it and what would be the best way to integrate that.
It would be helpful to add HTTP headers to BackgroundDownloadTask
since some URLs may require the Authorization
header (or others) in order to download them.
I'd like to use this package in my project as a replacement for dio.put().
Currently I have to pass a filename to upload(), but it would be nice to add a way to pass a Uint8List directly instead.
I am experiencing a problem in my implementation of the background_downloader.
My setup:
I have implemented my own queue database with queued files for download. Once an item is added to the queue, a worker (that subscribes to queue changes) will initiate a download (if no other downloads are active).
The same worker will on initialisation start a download (if no other downloads are active).
A queue item will have an ID, and this id is used as taskId for the background_downloader download task.
My problem
Below is the steps used to produce the issue:
taskId = x
(not literarily x, but for the sake of simplicity x is used here)taskId = x
(same id as in step 1)In the debug console, the following information is printed:
2 flutter: TaskStatus.enqueued 2 flutter: TaskStatus.running flutter: FileDownloader>FINE: 2023-04-04 15:18:51.204115: Could not delete temp file C:\Users\FLUTTE~1\AppData\Local\Temp\com.bbflight.background_downloader3078630720 flutter: TaskStatus.canceled
The downloader reports that the task is cancelled, and does not emit running status even though the download progress is clearly working. Finally it emits complete status when the download is complete and the file is successfully downloaded.
Is this a bug, or is the problem that I am not using a unique id for every download?
And do you have an idea why Windows is not able to delete the temp file?
After adding background_downloader
and trying to run the app I got
> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
> Duplicate class androidx.lifecycle.ViewModelLazy found in modules jetified-lifecycle-viewmodel-ktx-2.3.1-runtime (androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1) and lifecycle-viewmodel-2.5.0-runtime (androidx.lifecycle:lifecycle-viewmodel:2.5.0)
Duplicate class androidx.lifecycle.ViewTreeViewModelKt found in modules jetified-lifecycle-viewmodel-ktx-2.3.1-runtime (androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1) and lifecycle-viewmodel-2.5.0-runtime (androidx.lifecycle:lifecycle-viewmodel:2.5.0)
Go to the documentation to learn how to <a href="d.android.com/r/tools/classpath-sync-errors">Fix dependency resolution errors</a>.
There was probably a conflict with some other dependency I was using. So, I followed the suggestion from https://stackoverflow.com/a/69832319 and added following code inside dependencies{...}
in build.gradle (app level)
file
def lifecycle_version = "2.4.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
I was thinking it would be cool to add this info in Readme.md of this package (maybe between Initial setup
section and Limitations
section). It could save a lot of time and hassle for others like me.
Shoutout to the author for an amazing plugin and great documentation!
Just one thing that I think is unclear in the documentation:
On Android, once started (i.e. TaskStatus.running), a task must complete within 8 minutes
Does this mean that a download MUST complete within 8 minutes on Android, regardless of whether the app is in the foreground or the background?
We are developing an app where the user must be able to download video files for offline use (as many of our customers do not have regular access to WiFi). These video files can easily be 800-1000 MB. Does this mean that their internet speed must be minimum 16,67 Mbps to theoretically complete a 1GB download on Android?
1000MB / 8 min / 60 sek = 2,08 MB/s Internet speed: 2,08 * 8 bits = 16,67 Mbps
I am giving a try to this package from inside FlutterInAppWebView package,
When I add the package to my pub_spec.yaml
, I cant run my project anymore.
When I remove the package, my app is running properly.
Any idea please?
LoadError - dlopen(/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.4/lib/ffi_c.bundle, 0x0009): tried: '/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.4/lib/ffi_c.bundle' (mach-o file, but is an incompatible architecture (have (x86_64), need (arm64e))) - /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.4/lib/ffi_c.bundle
I would like to have a callback when the user has clicked on a notification. So that the user can interact with the downloaded file like basically just opening it.
Hi,
I've noticed that sometimes the getMimeType
function in Android returns an incorrect MIME type, which can cause issues when trying to insert media files into a specified destination. I've modified the code to correctly get the MIME type from the file name, and added an optional mimeType parameter that can be set when moving files to ensure that the correct mime type is used even if not determined easily.
I have a use case, where I need to be able to add form data to the multipart/form-data upload request.
We have a lot of images that will be uploaded and each has associated data that needs to be sent with it.
It would be great if the UploadTask object could have a Map<String, String> property for adding this.
On desktop, cancelling a task that will fail (eg invalid url) right after it is enqueued and before the download isolate has been created will not cancel the task, as it never gets to transferBytes
, where cancellation is tested
I am downloading a 10M file, but the file server may be far away from the user. For example, the file server is in China, but the user may be in England. Users are waiting for the file download is complete, if timeoutIntervalForResource is very large, the user may have been waiting for the download page. At this time, we may set the timeout time to 60 seconds. After 60 seconds, if the file server does not respond, it indicates that the request time out is requested. We need to inform the user that the network is abnormal and need to re-download. I think timeoutIntervalForResource this parameter should be exposed to developers, by a business to determine how much the value should be
Hi thanks for this awesome alternative package.🙏
I am thinking to switch from flutter downloader, as this package meets all of my requirements.
I have a question though which bits confuses me. Our app is complete offline app after file downloads and mostly used images and audio and very less video file.
In the app we have the list of task to be downloaded and hence it's exact length. I just want to Query if all the task has been downloaded (don't need progress for individual task) , but check for all file download completes.
So that later I can test the list of original download task and the completed Task have <= length of original list.l, that confirm that although some task failed but 90% of task are completed and I want to route to another page.
Thank-you and hope to get your response
It would be a valuable addition to include support for the Library directory, which is typically used to store persistent files on iOS.
defining new enum libraryDirectory
...
case BaseDirectory.libraryDirectory:
baseDir = await getLibraryDirectory();
break;
When I was trying the library I noticed that the notification is showing a strange file name which is derived from the download URL.
Therefore I would ask you to respect (as far as possible, I would guess impossible on iOS) to take the filename dynamically (when given) from the Content-Disposition
response header.
The plugin defaults to using a POST
. My backend requires these uploads to be a PUT
, though.
So it would be nice to be able to switch these over. The post
field on the UploadTask
is used to differentiate between binary and multi-part uploads. On the DownloadTask
the field is used to trigger a POST
instead of a GET
and contains the post body data.
Maybe this could be cleaned up a bit?
On Android, the filename is always the same downloadfile
followed by its suffix.
As for iOS, the filename is correctly deducted from the \filestream sent from the server.
On iOS when I call enqueue
notification permission is requested regardless the config. The problem is in Downloader.swift#94 the notificiationConfigJsonString
variable holds "null"
for me, then it is checked againts nil
after, so I get into the permission requesting if.
The origin of the problem is in native_downloader.dart because that arg is json encoded even if its null, so that's why the null is passed as string. Real dart null is converted to NsNull on Swift side correctly.
On android its not noticable because not much happens based on the config. Shouldn't permission be requested from Android 13 though? 🤔
Created a PR for it.
Basically, similar to how I can enqueue a single task and handle progress and status updates centrally via the listeners, can I do the same with a batch?
My use case requires me to track the single files individually anyway. But enqueuing them as a batch would make this a bit easier.
My issue is rather just a question so I could know does it have any sense to migrate to your package from flutter_downloader which has some issue with that fluttercommunity/flutter_downloader#488
If you want to support longer running downloads, could you use setForeground()
to mark it as a long running task?
https://developer.android.com/topic/libraries/architecture/workmanager/advanced/long-running
Currently, files have to be copied to a local directory (BaseDirectory) for uploading, which seems very inefficient for uploading large files. Being able to specify a direct path for uploading seems essential, especially for desktop apps. It would be nice to be able to specify the path directly in the directory parameter by setting BaseDirectory to none. (or any better way than this, if possible)
The current way works well for downloads, but seems very inefficient for uploads.
The Http package is now updated to the 1.0.0 stable version.
Because background_downloader 7.1.0 depends on http ^0.13.0 and no versions of background_downloader match >7.1.0 <8.0.0, background_downloader ^7.1.0 requires http ^0.13.0.
So, because filecast_mobile_flutter depends on both http ^1.0.0 and background_downloader ^7.1.0, version solving failed.
I supposed that the downloaded files would be present in the Files
app and downloads
folder for iOS but this it not the case. How can I downloaded them in that folder please?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.