diff --git a/docs/hooks/useDownloadBlob.md b/docs/hooks/useDownloadBlob.md new file mode 100644 index 0000000..a84a14d --- /dev/null +++ b/docs/hooks/useDownloadBlob.md @@ -0,0 +1,57 @@ +--- +tags: + - hook +--- + +# `useDownloadBlob` Hook + +`useDownloadBlob` hook is used to directly download a file as bytes from Firebase Storage. Instead of [`useDownloadLink` hook](./useDownloadLink.md), this hook might be useful to process a file byte by byte instead of directly downloading to the user's machine. + +A very simple example would be: + +```typescript +const reference = ref(storage, "path/to/remote/file.png"); +const { dispatch } = useDownloadBlob({ reference }); +const blob = await dispatch(file); +``` + +!!! warning + `useDownloadBlob` is lazy by default and will not do anything until you use `dispatch` function. + +You can get the state of the progress with this example. + +```typescript +const { state } = useDownloadBlob({ reference }); +const blob = await dispatch(); +// `state` is "ready" | "loading" | "done" +``` + +`dispatch` method will return an instance of [`Blob`][BlobDoc], but additionally, you can also listen to [`Blob`][BlobDoc] from `useDownloadBlob` hook as well: + +```typescript +const { blob } = useDownloadBlob({ reference }); +// blob updates and rerenders when the state is `"done"` +// until then, it is `undefined` +await dispatch(); +``` + +## Input Parameters + +Input parameters for `useDownloadBlob` hook is as follows: + +| Name | Type | Description | Required | Default Value | +|---|---|---|---|---| +| `reference` | [`firebase/storage/StorageReference`][StorageReferenceRefDoc] | Reference to a file in Storage. | ✅ | - | + +## Return Type + +`useDownloadBlob` hook returns an object with properties as below: + +| Name | Type | Description | +|---|---|---| +| `blob` | [`Blob`][BlobDoc] or `undefined` | The blob of remote file to read. `undefined` if the `state` is not `"done"`. | +| `state` | `"ready" | "loading" | "done"` | The state of the process. | +| `dispatch` | `(maxDownloadSizeBytes? number) => Promise` | A callback to start the process and return the link. You can also define how many bytes to download at most. | + +[StorageReferenceRefDoc]: https://firebase.google.com/docs/reference/js/storage.storagereference +[BlobDoc]: https://developer.mozilla.org/en-US/docs/Web/API/Blob \ No newline at end of file diff --git a/docs/storage/downloading-a-file.md b/docs/storage/downloading-a-file.md index 5136971..98e8bbb 100644 --- a/docs/storage/downloading-a-file.md +++ b/docs/storage/downloading-a-file.md @@ -2,5 +2,6 @@ You can use one of the following ways to download a file from Firebase Storage: - - [`useDownloadLink` hook](../hooks/useDownloadLink.md) + - [`useDownloadLink` hook](../hooks/useDownloadLink.md) to get the link to download + - [`useDownloadBlob` hook](../hooks/useDownloadBlob.md) to directly download the bytes to process it yourself - [`StorageDownloadLink` component](../components/StorageDownloadLink.md) \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 25d62b9..ed11962 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -80,6 +80,7 @@ nav: - hooks/useUploadFile.md - hooks/useUploadFileResumable.md - hooks/useDownloadLink.md + - hooks/useDownloadBlob.md - Components: - Firestore: - components/FirestoreDocument.md diff --git a/src/storage/index.ts b/src/storage/index.ts index 1c7a03a..7f76c3b 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -10,3 +10,5 @@ export * from "./useUploadFileResumable"; export * from "./useDownloadLink"; export * from "./StorageDownloadLink"; + +export * from "./useDownloadBlob"; diff --git a/src/storage/useDownloadBlob.test.ts b/src/storage/useDownloadBlob.test.ts new file mode 100644 index 0000000..c1addd4 --- /dev/null +++ b/src/storage/useDownloadBlob.test.ts @@ -0,0 +1,17 @@ +// Copyright (c) 2024 Eray Erdin +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +import { renderHook } from "@testing-library/react"; +import { ref } from "firebase/storage"; +import { useDownloadBlob } from "."; +import { storage } from "../firebase"; + +const reference = ref(storage, "files/README.md"); + +it("initially, useDownloadBlob hook should have ready state", async () => { + const { result } = renderHook(() => useDownloadBlob({ reference })); + const { state } = result.current; + expect(state).toBe("ready"); +}); diff --git a/src/storage/useDownloadBlob.ts b/src/storage/useDownloadBlob.ts new file mode 100644 index 0000000..b78be8b --- /dev/null +++ b/src/storage/useDownloadBlob.ts @@ -0,0 +1,38 @@ +// Copyright (c) 2024 Eray Erdin +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +import { StorageReference, getBlob } from "firebase/storage"; +import { useState } from "react"; + +type UseDownloadBlobParams = { + reference: StorageReference; +}; + +type UseDownloadBlobState = "ready" | "loading" | "done"; +type UseDownloadBlobDispatcher = ( + maxDownloadSizeBytes?: number, +) => Promise; +type UseDownloadBlob = { + blob: Blob | undefined; + state: UseDownloadBlobState; + dispatch: UseDownloadBlobDispatcher; +}; + +export const useDownloadBlob = ({ + reference, +}: UseDownloadBlobParams): UseDownloadBlob => { + const [state, setState] = useState("ready"); + const [blob, setBlob] = useState(undefined); + + const dispatch: UseDownloadBlobDispatcher = async (maxDownloadSizeBytes) => { + setState("loading"); + const blob = await getBlob(reference, maxDownloadSizeBytes); + setState("done"); + setBlob(blob); + return blob; + }; + + return { blob, state, dispatch }; +};