Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

boby's avatar
Level 2

Safari force pdf file download

Hello, I have an app laravel + vuejs in which users can download offer pdf file like this:

    public function show($id): JsonResponse|BinaryFileResponse
    {
        $offer = (Offer::findOrFail($id)) ?? NULL;
        $file_path = '/offers/' . $offer->pdf_file;
        if(!Storage::disk('local')->exists($file_path)) {
            return response()->json(['error' => 'File not found'], 404);
        } else {
            return response()->file(storage_path('/app/') . $file_path, headers: ['Content-Type' => 'application/pdf']);
        }
    }

in vuejs I fetch it like:

const response = await axios.get(`/api/offer/${id}`, {responseType: 'arraybuffer'});
let blob = new Blob([response.data], {type: 'application/pdf'});
let link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
window.open(link.href, '_blank');

This works fine in Chrome, it opens selected offer in new tab or download it on mobile. In Safari on iPhone nothing happens when I click the link. Now I saw online there are some solutions but mostly for file download, but I want it to open in new tab (at least on desktop). This might work but it will download file:

link.download = 'myfile.pdf';
link.click();

This is probably more like question for vuejs forum but maybe there is something I can do on laravel part to force the file download.

Thx!

0 likes
15 replies
Snapey's avatar

Why don't you use a normal href to download the file?

boby's avatar
Level 2

@Snapey but I need file to open not to download. What do you mean by "normal" href?

boby's avatar
Level 2

@Snapey something like:

<a href="myfile.pdf"> View offer </a>

on frontend? Not to send the file from laravel but the file location of it in storage?

Snapey's avatar

@boby No, like a URL that calls your controller method and returns the file.

MohamedTammam's avatar

Force it to download

 public function show($id): JsonResponse|BinaryFileResponse
    {
        $offer = (Offer::findOrFail($id)) ?? NULL;
        $file_path = '/offers/' . $offer->pdf_file;
        if(!Storage::disk('local')->exists($file_path)) {
            return response()->json(['error' => 'File not found'], 404);
        } else {
			return Storage::disk('local')->download($file_path);
        }
    }

https://laravel.com/docs/9.x/filesystem#downloading-files

Snapey's avatar

@MohamedTammam I mean, Issue an ajax request, and get back a blob of download then you have to handle it yourself in Javascript rather than letting the browser deal with it automatically.

boby's avatar
Level 2

@MohamedTammam thank you for your reply. I want to open the file in new tab (on desktop), not to download it. I belive to achive that, I need to send it via controller to frontend and then open it. Or maybe there is some other way.

boby's avatar
Level 2

@MohamedTammam I would rather not to put it to public storage because of the access, but if there is no other way, I will have to

Snapey's avatar

@boby You can keep your controller method to return the data. Just don't request it with javascript

boby's avatar
Level 2

@Snapey but how should I request it? I use vuejs on frontend, so it is javascript.

boby's avatar
Level 2

I changed on my FE:

<a @click="showOffer(offer.id, offer.offerNumber)" title="View">
    <i class="align-middle" data-feather="eye"></i>
</a>

to

<a :href="showOffer(offer.id, offer.offerNumber)" title="View">
    <i class="align-middle" data-feather="eye"></i>
</a>

so instead a click event I set href value in my function:

        async showOffer(id, offerNumber) {
            let data = {
                'id': id,
                'offerNumber': offerNumber.replace('/', '-')
            }
            try {
                const response = await axios.get(`/api/offer/${data.id}`, {responseType: 'arraybuffer'});
                let blob = new Blob([response.data], {type: 'application/pdf'});
                return window.URL.createObjectURL(blob);
            } catch (error) {
...
                });
            }
        },

but this now points to http://localhost/[object Promise]

@snapey is this what you meant by normal href or I am still missing the point?

Please or to participate in this conversation.