DISCLAIMER: Before creating this question, I've checked here, here and here, and also checked Laravel docs.
Context
- Laravel 9 full-stack
- No JS framework on front-end, which means I'm using vanilla JS
- The folders on Storage are setted like this:
- storage
- app
- public
- folder1
- folder1A
- folder1B
- folder1C
- etc
- folder1
- public
- app
- storage
- The files stored in each
folder1X
are.pdf
format and I don't know its names. - No folders are empty, nor with invalid/corrupted files.
The problem
I have a FileController.php
to download files that are inside a folder1X/
directory. The method to download it is as follows:
public function downloadFileFromStorage(Request $request): mixed
{
$dirpath = $request->dirpath; // dirpath = public/folder1/folder1X.
$files = Storage::allFiles($dirpath);
return response()->download(storage_path('app\\' . $files[0]));
}
(Note: dirpath is sent in a axios request by client and is also fetched from database on a previous request)
My Javascript CLI needs to enable the download of this file. The download is enabled by clicking on a button. The button calls downloadPDF(dirpath)
which works as follows:
function downloadPDF(dirpath) {
axios.post('/download-pdf-file', { dirpath })
.then(
success => {
const url = success.data
const a = document.createElement('a')
a.download = 'file.pdf'
a.href = url
a.click()
},
error => {
console.log(error.response)
}
)
}
But, when I run this function, I get a about:blank#blocked
error.
Attempts
- Changed the
a
HTML DOM approach to awindow.open(url)
on client; - Changed
response()
toStorage::download($files[0], 'file-name.pdf')
, and with this I also tried usingBlob
on client as follows:
success => {
const blob = new Blob([success.data], { type: 'application/pdf' })
const fileURL = URL.createObjectURL(blob)
window.openURL(fileURL)
},
- Also mixed
Blob
with thea
HTML DOM approach; - Changed
storage_path
argument to/app/public/
before concatenating to$files[0]
.
UPDATE
Following tips from @BenGooding and @cengsemihsahin, I changed files to the following:
JS
// FileDownload is imported on a require() at the code beginning
function downloadPDF(dirpath) {
axios({
url: '/download-pdf-file',
method: 'GET',
responseType: 'blob',
options: {
body: { dirpath }
}
}).then(
success => {
FileDownload(success.data, 'nota-fiscal.pdf')
}
)
}
PHP:
public function downloadFileFromStorage(Request $request): mixed
{
$dirpath = $request->dirpath; // dirpath = public/folder1/folder1X.
$files = Storage::allFiles($dirpath);
return Storage::download($files[0], 'filename.pdf');
}
and now it downloads a corrupted PDF that can't be opened.
CodePudding user response:
Finally found the issue, and it was here:
axios({
url: '/download-pdf-file',
method: 'GET',
responseType: 'blob',
options: { // here
body: { dirpath } // here
}
})
Laravel's Request arrow operator ->
can't fetch a GET body sent through options
(At least, not on $request->key
fashion; see more about it here) thus making me download a corrupted file - it wasn't fetching any file on Laravel as it didn't get any path at all.
Here are the solution I came with:
As I want to get a file in a route that doesn't change except for the 1X
at folder1X
, I'm processing the path obtained and sending the 1X
as a GET query param:
let folderNumber = dirpath.split('/')
folderNumber = folderNumber[folderNumber.length].replaceAll('/', '')
axios({
url: '/download-pdf-file?folder=',
method: 'GET',
responseType: 'blob'
})
This way I don't pass the whole path to back-end and it's possible to get folderNumber
by using $request->query()
:
public function downloadFileFromStorage(Request $request): mixed
{
$folderNumber = $request->query('folderNumber');
$folderPath = '/public/folder1/folder' . $folderNumber . '/';
$files = Storage::allFiles($folderPath);
return Storage::download($files[0], 'file-name.pdf');
}
In a nutshell:
- To download files, use
GET
requests; - To send arguments to
GET
requests, use query parameters and fetch them with$request->query('keyname')
(or find out another way. Good luck!);