Yeap, I know, that we can set storeInDownloads into addAndroidDownloads. But in my case, it doesn't work.
I should use a dynamic filename from the API headers (Content-Disposition) and I write a function to download the file and move it to another folder with the final filename/path, if this is correct, but works.
On iOS +/- I implemented with react-native-share, but on Android, it always saves in the app folder Download: /storage/emulated/0/Android/data/com.awesome.staging/files/Download/final-name.pdf
How I see in this case, after download the file moves to the root Download on Android, how can it be implemented? There is no flag storeInDownloads for addCompleteDownload function. So in the final result, I should always take the filename from headers on both platforms, please help.
import { useCallback, useState } from 'react';
import { Platform } from 'react-native';
import RNBlobUtil, { ReactNativeBlobUtilConfig } from 'react-native-blob-util';
import Share, { ShareOptions } from 'react-native-share';
import config from '../config';
const isAndroid = Platform.OS === 'android';
const isIos = Platform.OS === 'ios';
type DownloadPDFProps = {
id: string;
};
function getFilePath(fileName: string) {
const dir = isIos ? RNBlobUtil.fs.dirs.DocumentDir : RNBlobUtil.fs.dirs.DownloadDir;
return `${dir}/${fileName}`;
}
const mimeType = 'application/pdf';
const getFileNameFromContentDisposition = (
contentDisposition: string | undefined,
): string | null => {
if (!contentDisposition) return null;
const fileNameMatch = contentDisposition.match( /filename\*?=["']?(?:UTF-8'')?([^;"']+)/i);
return fileNameMatch ? decodeURIComponent(fileNameMatch[1]) : null;
};
/*
Downloads a PDF.
On iOS opens a "save to Files" sheet to choose where to store the file.
Android use DownloadManager to save into Download folder.
*/
export const useDownloadPDF = () => {
const [isPDFLoading, setIsPDFLoading] = useState(false);
const downloadPDF = useCallback(async ({ id }: DownloadPDFProps) => {
if (!id) return;
const tempFileName = 'temp_file.pdf';
const url = `${baseURL}/${id}/blank?format=pdf`;
const tempFilePath = getFilePath(tempFileName);
let finalPath = '';
setIsPDFLoading(true);
const blobConfig: ReactNativeBlobUtilConfig = {
fileCache: true,
path: tempFilePath,
overwrite: true,
};
try {
const resp = await RNBlobUtil.config(blobConfig).fetch('GET', url, {});
const contentDisposition = resp.info().headers['Content-Disposition'] || resp.info().headers['content-disposition'];
const apiFileName = getFileNameFromContentDisposition(contentDisposition) || tempFileName;
finalPath = getFilePath(apiFileName);
if (tempFilePath !== finalPath) {
await RNBlobUtil.fs.mv(tempFilePath, finalPath);
if (isAndroid) {
await RNBlobUtil.android.addCompleteDownload({
title: apiFileName,
description: 'PDF file',
mime: mimeType,
path: finalPath,
showNotification: true,
});
}
}
if (isIos) {
const shareOptions: ShareOptions = {
type: mimeType,
url: finalPath,
saveToFiles: true,
failOnCancel: false,
filename: apiFileName,
};
await Share.open(shareOptions);
}
} catch (error) {
console.error('Error downloading PDF:', error);
throw error;
} finally {
setIsPDFLoading(false);
try {
await RNBlobUtil.fs.unlink(finalPath);
} catch (cleanupError) {
console.error('Could not delete temp file:', cleanupError);
}
}
}, []);
return { downloadPDF, isPDFLoading };
};