I am using Expo 54 and when i make request for the first time when the cache of App/Expo Go is empty. It takes about 1-5 minutes for the response to come back after the response of the first download, it takes less than 2-3 seconds for any URL (not just the same one).
Log on First Download (cache is empty):

Second Download (same case for any file)

Code
import * as FileSystem from "expo-file-system/legacy";
import { Paths, File } from "expo-file-system";
const handleDownload = async (item, subject) => {
const startTotal = Date.now();
dispatch(setNote({ name: "loading", value: true }));
let cacheFile = null;
try {
const url = item?.noteUrl;
if (!item || !url) {
Alert.alert("Error", "File URL not found.");
return;
}
// 🧾 File name & extension
let name = item.name || "file";
let noteType = item.noteType;
let ext = getExtensionName(url) || "pdf";
name = name
.replace(/[/\\?%*:|"<>.]/g, "_")
.trim()
.replace(/\s+/g, "_");
const filename = `${name}.${ext}`;
const subjectName = subject.name;
console.log(`📘 Downloading for subject: ${subjectName}`);
// 🕒 1️⃣ Download using expo/fetch
const startDownload = Date.now();
// 1️⃣ Using Fetch
const response = await fetch(url);
if (!response.ok)
throw new Error(`HTTP error! Status: ${response.status}`);
console.log("Response:");
console.log(response);
cacheFile = new File(Paths.cache, filename);
await cacheFile.write(await response.bytes());
const cacheUri = cacheFile.uri;
const endDownload = Date.now();
console.log(
`⏱️ Download took: ${((endDownload - startDownload) / 1000).toFixed(
2
)}s`
);
// 🧭 Android: Save to folder
if (Platform.OS === "android") {
console.log("📂 Platform: Android");
// 🕒 2️⃣ Folder selection / creation
const startPicker = Date.now();
const graderUri = await getFolderUri(["Notes", subjectName]);
const endPicker = Date.now();
console.log(
`📁 Folder setup took: ${((endPicker - startPicker) / 1000).toFixed(
2
)}s`
);
console.log(`📍 Final destination URI: ${graderUri}`);
// 🕒 3️⃣ File creation
const startCreate = Date.now();
const newUri = await FileSystem.StorageAccessFramework.createFileAsync(
graderUri,
filename,
noteType || "application/pdf"
);
const endCreate = Date.now();
console.log(
`📄 File creation took: ${((endCreate - startCreate) / 1000).toFixed(
2
)}s`
);
// 🕒 4️⃣ Read cache file
const startRead = Date.now();
const cacheFileBytes = await cacheFile.bytes();
const endRead = Date.now();
console.log(
`📖 Reading cache file took: ${((endRead - startRead) / 1000).toFixed(
2
)}s`
);
// 🕒 5️⃣ Write to SAF destination
const startWrite = Date.now();
const file = new File(newUri);
file.write(cacheFileBytes);
const endWrite = Date.now();
console.log(
`✍️ Writing to destination took: ${(
(endWrite - startWrite) /
1000
).toFixed(2)}s`
);
Alert.alert("Success ✅", "File saved to folder");
} else {
// 🧭 iOS / Web
if (await Sharing.isAvailableAsync()) {
await Sharing.shareAsync(cacheUri, {
mimeType: noteType || "application/pdf",
});
} else {
Alert.alert("Downloaded ✅", `Saved to app cache: ${cacheUri}`);
}
}
} catch (error) {
console.error("❌ Download error:", error);
Alert.alert("Error ❌", error.message || "Could not save to folder.");
} finally {
// 6️⃣ 🧹 Always clean up cache
if (cacheFile) {
try {
const startDelete = Date.now();
await cacheFile.delete();
const endDelete = Date.now();
console.log(
`🧹 Cache deletion took: ${(
(endDelete - startDelete) /
1000
).toFixed(2)}s`
);
} catch (e) {
console.warn("⚠️ Failed to delete cache file:", e.message);
}
}
dispatch(setNote({ name: "loading", value: false }));
const endTotal = Date.now();
console.log(
`✅ Total time: ${((endTotal - startTotal) / 1000).toFixed(2)}s`
);
}
};