I'm using React localStorage to save the paths to image/video files. The paths are correct since the images/videos display properly in their given components. The paths also save correctly to storage because when I pull from storage, I see they are the same as what was saved.
However, the image path only "lasts" for a few refreshes or back buttons, and then suddenly, the image won't be found anymore, even though the path is unchanged. The video is never "found" after any refresh or back button (even though the path is correctly fetched) — it always says the video can't be found.
If the browser is closed and reopened, neither image nor video is found anymore.
I get the path using an input component like so:
<input
ref={inputRef}
type="file"
accept="image/*,video/mp4,video/x-m4v,video/*,audio/x-m4a,audio/*"
name="image"
onChange={(event) => loadFileFromDevice(event)}
/>
I save the path to both state and localStorage like so:
const loadFileFromDevice = (event: any) => {
let media = event.target.files[0];
if (media === null || media === undefined)
// Means you canceled
return;
let path = URL.createObjectURL(media);
let isImg = media.type.indexOf("image") != -1;
if (isImg) {
setImgSrc(path);
setVideoSrc("");
localStorage.setItem(IMG_KEY, path);
} else {
setImgSrc("");
setVideoSrc(path);
localStorage.setItem(VIDEO_KEY, path);
}
};
I initialize state using the following:
// Outside component
const getValue = (storageKey: string, defaultVal: string) => {
const item = localStorage.getItem(storageKey);
if (item === null || item === undefined || item.length === 0) return defaultVal;
return item;
};
// Inside component
const [imgSrc, setImgSrc] = useState(getValue(IMG_KEY, ""));
const [videoSrc, setVideoSrc] = useState(getValue(VIDEO_KEY, ""));
I render the image and video in the following components:
<img src={imgSrc} width="100%" height="100%" />
<video src={videoSrc} width="100%" height="100%" autoPlay loop muted />
As mentioned, the path is fetched fine and renders the images/videos correctly as long as the browser isn't refreshed. If I do refresh, the images will still be found (usually) but not the video.
If I refresh a few more times or let a few minutes pass and then refresh, the image will no longer be found. If I close and reopen the browser, neither image nor video is found. The file locations have not changed.
Why can't I reload the image/video I selected after a refresh/back button/close?
CodePudding user response:
So you are creating the ephemeral URL to the media - it's lifecycle is tied to the document
object. After reloading the page, saved url is no longer valid - it is not pointing to the file anymore. Via MDN
The URL lifetime is tied to the document in the window on which it was created. The new object URL represents the specified File object or Blob object.
I'd recommend using IndexedDB To achieve your goal.
CodePudding user response:
A URL created with URL.createObjectURL()
points to an in-memory file, not the one on the hard drive of the user. And it has a limited lifetime, as they say on MDN's documentation:
The URL lifetime is tied to the document in the window on which it was created. The new object URL represents the specified File object or Blob object.
A common way to keep files is through cached responses. For example, the below code would work in vanilla HTML, CSS, and JavaScirpt, which you could easily adapt to React:
<input type="file" name="image" id="" />
<img src="" alt="" />
const createResponseObjectAndSaveInCache = (media) => {
const response = new Response(media, {
status: 200,
statusText: "Ok",
headers: {
"content-type": media.type,
"content-length": media.size,
"X-file": media.name,
},
});
window.caches.open("cachename").then((cache) => {
let name = response.headers.get("X-file");
let url = new URL(`/${Date.now()}/${name}`, location.origin);
cache.put(url, response);
localStorage.setItem("media", url.href);
});
};
const displayFromCache = async () => {
const href = localStorage.getItem("media");
const cache = await caches.open("cachename");
let response = await cache.match(href);
if (!response) {
return;
}
let blob = await response.blob();
document.querySelector("img").src = URL.createObjectURL(blob);
};
document.querySelector("input").addEventListener("change", (e) => {
let media = event.target.files[0];
if (media) {
document.querySelector("img").src = URL.createObjectURL(media);
createResponseObjectAndSaveInCache(media);
}
});
// display cached media if there is on load
displayFromCache();