knuckleswtf / scribe

Generate API documentation for humans from your Laravel codebase.✍
https://scribe.knuckles.wtf/laravel/
MIT License
1.73k stars 311 forks source link

Download binary response file from Laravel API #900

Open Vitolete opened 3 weeks ago

Vitolete commented 3 weeks ago

Scribe version

4.37

Your question

Hello, I just wanted to know if there is any way to download a zip file that comes in binary format and is obtained from a Laravel API response using response()->download().

I have tried doing it with FileSaver and transforming it to a Blob, but the downloaded files are corrupted.

return preflightPromise.then(() => makeAPICall(method, path, body, query, headers, endpointId))
                    .then(([responseStatus, statusText, responseContent, responseHeaders]) => {
                        responsePanel.hidden = false;
                        responsePanel.querySelector(`.response-status`).textContent = responseStatus + " " + statusText ;

                        let contentEl = responsePanel.querySelector(`.response-content`);
                        if (responseContent === '') {
                            contentEl.textContent = contentEl.dataset.emptyResponseText;
                            return;
                        }

                        const blob = new Blob([responseContent], { type: 'application/zip' });
                        saveAs(blob, 'file.zip');

                        // Prettify it if it's JSON
                        let isJson = false;
                        try {
                            const jsonParsed = JSON.parse(responseContent);
                            if (jsonParsed !== null) {
                                isJson = true;
                                responseContent = JSON.stringify(jsonParsed, null, 4);
                            }
                        } catch (e) {}

                        contentEl.innerHTML = responseContent;
                        isJson && window.hljs.highlightElement(contentEl);
                    })
                    .catch(err => {
                        console.log(err);
                        let errorMessage = err.message || err;
                        errorPanel.hidden = false;
                        errorPanel.querySelector(`.error-message`).textContent = errorMessage;
                    })
                    .finally(() => { btnElement.disabled = false } );

In Swagger there's an option that creates a link to download the file, and it works correctly.

Thank you and best regards.

Docs

shalvah commented 1 week ago

Hey, don't know if you still need this, but my guess is that if you're doing this based on responseContent, it's possible the response body might already have been decoded as text, hence the corruption. You may need to capture the raw response bytes.

shalvah commented 1 week ago

Just checked, and yes, that's what's happening. In makeAPICall, we call Response.text() on the fetch response, which decodes it as a UTF-8 string.

https://github.com/knuckleswtf/scribe/blob/a26d9c912a6c42f19a133df1db571c802dbc47ee/resources/js/tryitout.js#L126

If you want the raw body, you need to edit this to response.blob().

shalvah commented 1 week ago

What you mentioned about providing the option to download sounds like a good feature. I can't commit to making any changes to this right now, though. Please send in a PR if you can!