The idea is as follows:
- Images/documents are stored privately on the server
- 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
- For each ModelA, numerous requests are made to endpoint to fetch images which are returned as
\Symfony\Component\HttpFoundation\StreamedResponse
viaStorage::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:
- Create an
octet-stream
url link withFileReader
but these wouldn't display and had the same url despiteawait
being used for thereader.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])
- 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" >
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>```