Sua solução é muito elegante, cumpre muito bem o papel.
Um pequeno exemplo de como eu fiz, adaptei para .csv:
Python / Fastapi
import csv
import io
import random
import string
def gerar_dados_csv(tamanho_desejado_mb):
data = io.StringIO()
writer = csv.writer(data)
writer.writerow(["ID", "NAME", "AGE", "COUNTRY"])
total_size = 0
while total_size < tamanho_desejado_mb * 1024 * 1024:
id = random.randint(1, 1000)
name = ''.join(random.choices(string.ascii_uppercase + string.digits, k=5))
age = random.randint(20, 60)
country = random.choice(['Brazil', 'India', 'USA', 'Canada', 'UK'])
writer.writerow([id, name, age, country])
chunk = data.getvalue()
total_size += len(chunk)
data.seek(0)
data.truncate(0)
yield chunk
@router.get('/csv/{tamanho}')
async def get_pdf(tamanho: int):
nome_arquivo = f'relatorio.csv'
headers = {"Content-Disposition": f"attachment; filename={nome_arquivo}", 'x-filename': nome_arquivo}
return StreamingResponse(gerar_dados_csv(tamanho), media_type='application/csv', headers=headers)
Javascript
/**
* Método para realizar download em uma nova aba, com responsabilidade
* do processo de download a cargo do browser.
*
* @param {String} url para download
*/
async function fileDownload(url) {
try {
// url completa, com path variables e query params.
const _url = `${axiosAPI.defaults.baseURL}${url}`
const anchorElement = document.createElement('a')
anchorElement.setAttribute("href", _url)
anchorElement.setAttribute("target", "_blank")
document.body.appendChild(anchorElement)
anchorElement.click()
document.body.removeChild(anchorElement)
}
catch (error) {
console.log('error: ', error)
throw new Error("Erro ao baixar arquivo.")
}
}