Next.js Discord

Discord Forum

how to use blob with next/image? issues with RSC

Answered
Chinese softshell turtle posted this in #help-forum
Open in Discord
Chinese softshell turtleOP
export default async function Profile(props: any) {
  let session = auth.getSession()
  const client = session.client
  const authenticated = await session.isSignedIn()

  let authData
  let publicData
  let imageBlob

  if (authenticated) {
    authData = await e
      .select(e.Post, (post) => ({
        name: true,
        photoUrl: true,
        filter: e.op(post.created_by, "?=", e.global.current_user),
      }))
      .run(client)

      const r2Client = new CloudflareR2Client({
        // Cloudflare AWS Credentials
        accessKeyId: "",
        secretAccessKey:
          "",
        // Cloudflare Account ID
        accountId: "",
        // Cloudflare Bucket name
        bucket: "",
      })
      imageBlob = await r2Client.get("github-profile")
      console.log(imageBlob, "img blob")
  } else {
    publicData = await e
      .select(e.Post, (post) => ({
        name: true,
      }))
      .run(client)
  }

  return (
    <>
      {authData && imageBlob && (
        <header className="flex justify-between items-center pb-4">
          <div>Authenticated data:</div>
          {JSON.stringify(authData)}
          <Image
            src={imageBlob}
            width={500}
            height={500}
            alt="Picture of the author"
            unoptimized
          />
        </header>
      )}


have above code
Answered by joulev
you cannot(*) fetch an image and render it in an <img> (or next/image for that matter) like that because <img> is fundamentally client side fetching. you give it an URL, it fetches the image on that URL in the client side, separate from the page.

so instead of fetching the file directly, you should instead generate a URL from which the browser can use to fetch the image. then use that generated URL as the src.

(*) technically you can, if you convert the image to base64 and render the <img> with the base64. I wouldn't recommend that though, it is not optimised.
View full answer

104 Replies

Chinese softshell turtleOP
i stipped creds but I get a Blob
from cloudflare R2 back
this if I log it
how do I pass it to next/image?
google says unoptimised should work, it does not
https://nextjs.org/docs/pages/api-reference/components/image#unoptimized
it wants String or StaticImport
fails too
gpt-4 rec'd I do

      imageBlob = await r2Client.get("github-profile")
      imageUrl = URL.createObjectURL(imageBlob)
im so confused, everyone says it should work
thats how you turn a file blob into something img or Image from next.js gets
do i have to base64 encode it or what
fails too
or maybe i should drop next/image for this?
now that i try <img i am getting this
  if (authenticated) {
    authData = await e
      .select(e.Post, (post) => ({
        name: true,
        photoUrl: true,
        filter: e.op(post.created_by, "?=", e.global.current_user),
      }))
      .run(client)

    const r2Client = new CloudflareR2Client({
      // Cloudflare AWS Credentials
      accessKeyId: "",
      secretAccessKey:
        "",
      // Cloudflare Account ID
      accountId: "",
      // Cloudflare Bucket name
      bucket: "",
    })
    imageBlob = await r2Client.get("github-profile")
    imageUrl = URL.createObjectURL(imageBlob)
on this code
import e from "@/dbschema/edgeql-js"
import { auth } from "@/edgedb-next-client"
import { CloudflareR2Client } from "cloudflare-r2-edge"
import Image from "next/image"

export default async function Profile(props: any) {
  let session = auth.getSession()
  const client = session.client
  const authenticated = await session.isSignedIn()

  let authData
  let publicData
  let imageBlob
  let imageUrl

  if (authenticated) {
    authData = await e
      .select(e.Post, (post) => ({
        name: true,
        photoUrl: true,
        filter: e.op(post.created_by, "?=", e.global.current_user),
      }))
      .run(client)

    const r2Client = new CloudflareR2Client({
      // Cloudflare AWS Credentials
      accessKeyId: "",
      secretAccessKey:
        "",
      // Cloudflare Account ID
      accountId: "",
      // Cloudflare Bucket name
      bucket: "",
    })
    imageBlob = await r2Client.get("github-profile")
    imageUrl = URL.createObjectURL(imageBlob)
    // imageUrl = blobToBase64(imageBlob)
  } else {
    publicData = await e
      .select(e.Post, (post) => ({
        name: true,
      }))
      .run(client)
  }

  return (
    <>
      {authData && imageBlob && (
        <header className="flex justify-between items-center pb-4">
          <div>Authenticated data:</div>
          {JSON.stringify(authData)}
          <img src={imageUrl} />
          {/* <Image
            src={imageUrl}
            width={500}
            height={500}
            alt="Picture of the author"
            unoptimized
          /> */}
        </header>
      )}
      {publicData && (
        <header className="flex justify-between items-center pb-4">
          <div>Public data:</div>
          {JSON.stringify(publicData)}
        </header>
      )}
    </>
  )
}

function blobToBase64(blob: any) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = function () {
      const dataUrl = reader.result
      const base64EncodedString = dataUrl.split(",")[1] // Extract base64 part from the Data URL
      resolve(base64EncodedString)
    }
    reader.onerror = function () {
      reject(new Error("Error converting blob to base64"))
    }
    reader.readAsDataURL(blob)
  })
}


full code
its like it doesn't want me to await inside RSC?
its async though
yea why can i not do await in rsc
its async
is err
@Chinese softshell turtle now that i try `<img` i am getting this
it could be that nextjs's simulated edge environment doesnt have the File class https://github.com/maccman/cloudflare-r2-edge/blob/master/src/index.ts#L77
Chinese softshell turtleOP
mm
is there another way to work around it
wait are you using edge runtime here?
Chinese softshell turtleOP
i dont mind forking the repo
i just am doing bun dev locally
not sure about whats happening
but like is there any export const runtime
Chinese softshell turtleOP
not in my code
ok then its not that
Chinese softshell turtleOP
is my code
i have an image stored in cloudflare
its a blob
actually i can store the blob in db
and have the the next.js get image from db
this should avoid this File business
but in what format should i store
so next is happy
i get a Blob
whatever that means
from r2
my db is postgres
@Chinese softshell turtle js export default async function Profile(props: any) { let session = auth.getSession() const client = session.client const authenticated = await session.isSignedIn() let authData let publicData let imageBlob if (authenticated) { authData = await e .select(e.Post, (post) => ({ name: true, photoUrl: true, filter: e.op(post.created_by, "?=", e.global.current_user), })) .run(client) const r2Client = new CloudflareR2Client({ // Cloudflare AWS Credentials accessKeyId: "", secretAccessKey: "", // Cloudflare Account ID accountId: "", // Cloudflare Bucket name bucket: "", }) imageBlob = await r2Client.get("github-profile") console.log(imageBlob, "img blob") } else { publicData = await e .select(e.Post, (post) => ({ name: true, })) .run(client) } return ( <> {authData && imageBlob && ( <header className="flex justify-between items-center pb-4"> <div>Authenticated data:</div> {JSON.stringify(authData)} <Image src={imageBlob} width={500} height={500} alt="Picture of the author" unoptimized /> </header> )} have above code
you cannot(*) fetch an image and render it in an <img> (or next/image for that matter) like that because <img> is fundamentally client side fetching. you give it an URL, it fetches the image on that URL in the client side, separate from the page.

so instead of fetching the file directly, you should instead generate a URL from which the browser can use to fetch the image. then use that generated URL as the src.

(*) technically you can, if you convert the image to base64 and render the <img> with the base64. I wouldn't recommend that though, it is not optimised.
Answer
Chinese softshell turtleOP
what is a good way to do this
i have image in r2
its like hash table
Chinese softshell turtleOP
s3
Chinese softshell turtleOP
i see
i agree with what joulev said, but i will add if every asset can be public you can skip the sign url step and just make bucket public
if the bucket is already public then simply provide the public URL to next/image or <img>. if the bucket is private then generate a signed URL, then use that URL for next/image or <img>
if you want to use the blob pattern, you still need an URL. then on the client side, fetch that URL manually, convert the body to blob, then you can use that blob for next/image and <img>. it has to be a client side blob, not a server side blob
Chinese softshell turtleOP
I have a signed url
https://kuskus.e1a1737083ffd849ad2638b7e6069dec.r2.cloudflarestorage.com/github-profile?X-Amz-Expires=3600&X-Amz-Date=20240501T150941Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=4b9db796d0eecc94ddc55e4681d21b0b%2F20240501%2Fauto%2Fs3%2Faws4_request&X-Amz-SignedHeaders=accept-encoding%3Bhost&X-Amz-Signature=379f324c8c9d25897286c136b774782298cf9c1981fcd62a76fec807eee3b43a
however its not public
even though it should be in theory
not sure what to do
thats the only options i have
hmm you have a domain then it should already be public
that error is because you must not have done signing correctly
no, this link as you said doesn't work. a link that works will start with images.kuskus.app
but i tried on your custom domain https://images.kuskus.com/github-profile, and it seems kinda broken?
Chinese softshell turtleOP
yep
seems to fail
is package i am using
used this code to upload the image as buffer
its here though
yeah then just <Image src="https://images.kuskus.app/github-profile" /> why not
Chinese softshell turtleOP
trying to resolve this now
remotePatterns i think i need
this i guess
or maybe it can just be domains
although its 2022 answer
yea that fails even on restart
there is this too i guess
but thats bit insecure
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'kuskus.app',
      },
    ],
  },
}

module.exports = nextConfig
what i have now
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: ['images.kuskus.app']
  }
}

module.exports = nextConfig
ok trying this
neat, it worked
ok thank you all ❤️
Chinese softshell turtleOP
not sure if there is something like /solved or?
yay thanks
@Chinese softshell turtle not sure if there is something like `/solved` or?
right click on message -> apps -> mark as solved