joltup / rn-fetch-blob

A project committed to making file access and data transfer easier, efficient for React Native developers.
MIT License
2.81k stars 772 forks source link

Progress Not Working - Download (Android) #697

Open scgough opened 3 years ago

scgough commented 3 years ago

Project package versions: rn-fetch-blob: 0.12.0 React: 16.11.0 RN: 0.62.2

Tested on: iOS: 14 Android: 11

Download Progress (Android):

I have a strange issue in that progress doesn't seem to be firing on Android for downloads. iOS works fine.

Download Usage:

const response = await RNFetchBlob.config({
    fileCache: true,
    path: targetPath,
    indicator: true,
    addAndroidDownloads: {
        useDownloadManager: true,
        notification: true,
        title: 'Download Complete',
        description: 'file description'
        mime,
        path: targetPath,
        mediaScannable: true,
    },
})
.fetch('GET', downloadURI)
.progress({ interval: 16 }, (written: number, total: number) => {
            //do something w/progress
});

I've done some digging around the java code but haven't found anything yet. I'm happy to contribute on this and add a PR so if anyone else is experiencing this issue and has any info let me know - let's try and get this sorted!

Blue-Lan commented 3 years ago

I encountered the same issue. When using .fetch().progress() instead of .config().fetch().progress() it works properly. I suspect the bound fetch function returned by config() might be the culprit, found in the js code. However I have not been able to find the root cause.

avonipenti commented 3 years ago

Digging through multiple download issues in Android, I also noticed progress not returning received and total bytes.

The culprit seemed to be

 if (reportConfig != null && reportConfig.shouldReport(progress /* progress */)) {
                        if (contentLength() != -1) {
                            // For non-chunked downloads
                            reportProgress(mTaskId, bytesDownloaded, contentLength());
                        } else {
                            // For chunked downloads
                            if (!isEndMarkerReceived) {
                                // THIS INDICATED WE ARE ALWAYS SENDING 0 and -1 for chunked downloads.
                                reportProgress(mTaskId, 0, contentLength()); 
                            } else{
                                reportProgress(mTaskId, bytesDownloaded, bytesDownloaded);
                            }
                        }
                    }

Not sure how I could change the download to a non-chunked one but testing by replacing that line with
reportProgress(mTaskId, bytesDownloaded, bytesDownloaded);

scgough commented 3 years ago

I think in this/my case it is down to the fact I have useDownloadManager: true set. When using Download Manager no progress event fires at all.

ZeeshanAhmadKhalil commented 2 years ago

and when removing the useDownloadManager application crashes!

leonineli commented 2 years ago

I modified RNFetchBlobReq.java to solve it. Add this:

    private final int QUERY = 1314;
    private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
    private Future<?> future;
    private Handler mHandler = new Handler(new Handler.Callback() {
        public boolean handleMessage(Message msg) {
            switch (msg.what) {

                case QUERY:

                    Bundle data = msg.getData();
                    long id = data.getLong("downloadManagerId");
                    if (id == downloadManagerId) {

                        Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();

                        DownloadManager downloadManager = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);

                        DownloadManager.Query query = new DownloadManager.Query();

                        Cursor cursor = downloadManager.query(query);

                        if (cursor != null && cursor.moveToFirst()) {

                            long written = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));

                            long total = cursor.getLong(cursor.getColumnIndex(
                                    DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                            cursor.close();

                            RNFetchBlobProgressConfig reportConfig = getReportProgress(taskId);
                            float progress = (total != -1) ? written / total : 0;

                            if (reportConfig != null && reportConfig.shouldReport(progress /* progress */)) {
                                WritableMap args = Arguments.createMap();
                                args.putString("taskId", String.valueOf(taskId));
                                args.putString("written", String.valueOf(written));
                                args.putString("total", String.valueOf(total));
                                args.putString("chunk", "");
                                RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                                        .emit(RNFetchBlobConst.EVENT_PROGRESS, args);

                            }

                            if (total == written) {
                                future.cancel(true);
                            }
                        }
                    }
            }
            return true;
        }
    });

@Override
public void run() {

        // use download manager instead of default HTTP implementation
        if (options.addAndroidDownloads != null && options.addAndroidDownloads.hasKey("useDownloadManager")) {

            if (options.addAndroidDownloads.getBoolean("useDownloadManager")) {
                ....
                // add this lines:
                future = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                    @Override
                    public void run() {
                        Message msg = mHandler.obtainMessage();
                        Bundle data = new Bundle();
                        data.putLong("downloadManagerId", downloadManagerId);
                        msg.setData(data);
                        msg.what = QUERY;
                        mHandler.sendMessage(msg);
                    }
                }, 0, 100, TimeUnit.MILLISECONDS);
                //add lines end.
                return;
            }
....
        }

//edit releaseTaskResource
 private void releaseTaskResource() {
       ...
        if (future != null && !future.isCancelled())
            future.cancel(true);
        if (scheduledExecutorService != null && !scheduledExecutorService.isShutdown())
            scheduledExecutorService.shutdown();
    }
ebnersilva commented 2 years ago

@leonineli i tried to implement your code, but, now i'm receiving incompatibility with gradle. I receive the error of i need update my code to Gradle 7.0

leonineli commented 2 years ago

@ebnersilva If it is difficult to change the code, you can use my fork, but I can't guarantee that it is the latest code.

yarn add li-rn-fetch-blob

garryalfa commented 2 years ago

hi @joltup, is there any chance that this fixes will be merged to your master?