Home > Software design >  How to correctly display images on React frontend that are returned by Laravel as StreamedResponse o
How to correctly display images on React frontend that are returned by Laravel as StreamedResponse o

Time:07-26

The idea is as follows:

  1. Images/documents are stored privately on the server
  2. A logged-in user on frontend clicks a button which sends an axios request to backend to get an aggregated result of ModelA from TableA and it's associated attachment file list from TableB
  3. For each ModelA, numerous requests are made to endpoint to fetch images which are returned as \Symfony\Component\HttpFoundation\StreamedResponse via Storage::download($request->file_name)

This works in the sense that files are returned.

Note - I tried attaching all files to response in step 2 but this didn't work, so added the extra step to get file list and get individual files after that based on the list. This might kill the webserver if the amount of requests becomes too high, so would appreciate any advise on a different approach.

The problem How to display the files in React and is this the right approach at all considering potential performance issues noted above?

I've tried the following:

  1. Create an octet-stream url link with FileReader but these wouldn't display and had the same url despite await being used for the reader.readAsDataURL(blob) function:
const { email, name, message, files } = props
const [previews, setPreviews] = useState<string[]>([])
const { attachments } = useAttachment(files)

useEffect(() => {
    const p = previews

    files && attachments?.forEach(async filename => {
      const reader = new FileReader()
    
      reader.onloadend = () => {
        p.push(reader.result as string)
        setPreviews(p)
      }
      const blob = new Blob([filename])
      await reader.readAsDataURL(blob)
    })
  }, [files, attachments, previews])
  1. Create src attributes with URL.createObjectURL() but these, although generated and unique, wouldn't display when used in an <img /> tag:
useEffect(() => {
    const p = previews

    files && attachments?.forEach(filename => {
      const blob = new Blob([filename])
      const src = URL.createObjectURL(blob)
      p.push(src)
      setPreviews(p)
    })
  }, [files, attachments, previews])

Results example:

<img src="blob:http://127.0.0.1:8000/791f5efb-1b4e-4474-a4b6-d7b14b881c28" >
<img src="blob:http://127.0.0.1:8000/3d93449e-175d-49af-9a7e-61de3669817c" >

As displayed in UI

Here's the useAttachment hook:

import { useEffect, useState } from 'react'
import { api } from '@utils/useAxios'

const useAttachment = (files: any[] | undefined) => {
  const [attachments, setAttachments] = useState<any[]>([])

  const handleRequest = async (data: FormData) => {
    await api().post('api/attachment', data).then(resp => {
      const attach = attachments
      attach.push(resp)
      setAttachments(attach)
    })
  }
  useEffect(() => {
    if (files) {
      files.forEach(async att => {
        const formData = new FormData()
        formData.append('file_name', att.file_name)
        await handleRequest(formData)
      })
    }
  }, [files, attachments])

  return { attachments }
}

export default useAttachment

CodePudding user response:

Try Storage::response(). This is the same as Storage::download() just that it sets the Content-Disposition header to inline instead of attachment. This tells the browser to display it instead of downloading it. See MDN Docs Here Then you can use it as the src for an <img/>.

CodePudding user response:

Solved it by sending the files in a single response but encoded with base64encode(Storage::get('filename')). Then, on the frontend, it was as simple as:

const base64string = 'stringReturned'

<img> src={`data:image/png;base64,${base64string}`}</img>```
  • Related