3

What's the best way to generate a download (in my case of a .txt file) on the fly? By this I mean without storing the file on the server before. For understanding here's what I need to get done:

public function getDesktopDownload(Request $request){

        $txt = "Logs ";

        //offer the content of txt as a download (logs.txt)
        $headers = ['Content-type' => 'text/plain', 'Content-Disposition' => sprintf('attachment; filename="test.txt"'), 'Content-Length' => sizeof($txt)];

        return Response::make($txt, 200, $headers);
}
wichtel
  • 181
  • 1
  • 3
  • 16

3 Answers3

6

you can use stream response to send content as download file

What's the best way to generate a download (in my case of a .txt file) on the fly? By this I mean without storing the file on the server before. For understanding here's what I need to get done:

use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\StreamedResponse;

use the above classes at top, then prepare content

$logs = Log::all();

$txt = "Logs \n";


foreach ($logs as $log) {
    $txt .= $logs->id;
    $txt .= "\n";
}

then send a stream of content as download

$response = new StreamedResponse();
$response->setCallBack(function () use($txt) {
      echo $txt;
});
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'logs.txt');
$response->headers->set('Content-Disposition', $disposition);

return $response;
Sagar Rabadiya
  • 4,126
  • 1
  • 21
  • 27
  • Nice solutuion! But for me it downloads a logs.txt.html file. Anyway to receive a pure .txt file? – zidanex Mar 29 '19 at 09:45
  • I got it. You have to add the content-type to the header like so: $response->headers->set('Content-Type', 'text/plain'); – zidanex Mar 29 '19 at 09:49
  • This will give the out-of-memory error as well, if the $txt variable contains more than your memory limit. What you *should* do is return part of the logfile each iteration. – aross Mar 12 '20 at 14:57
6

Try this

public function getDownload(Request $request) {
    // prepare content
    $logs = Log::all();
    $content = "Logs \n";
    foreach ($logs as $log) {
      $content .= $logs->id;
      $content .= "\n";
    }

    // file name that will be used in the download
    $fileName = "logs.txt";

    // use headers in order to generate the download
    $headers = [
      'Content-type' => 'text/plain', 
      'Content-Disposition' => sprintf('attachment; filename="%s"', $fileName),
      'Content-Length' => sizeof($content)
    ];

    // make a response, with the content, a 200 response code and the headers
    return Response::make($content, 200, $headers);
}
JCarlosR
  • 1,598
  • 3
  • 19
  • 32
Pratik Boda
  • 394
  • 2
  • 6
  • this will fail if the file size is more than allowed memory for php, so if php has 128MB memory and file size is greater than 128 mb then it will give fatal error about memory out, so instead using streamed response can solve large file problem. it will send file in chunks. check below answer. – Sagar Rabadiya Oct 17 '16 at 11:03
  • @Pratik Boda I edited code above, problem is, the file I get for download, just contains the letter "L". Any idea where things go wrong? – wichtel Oct 17 '16 at 11:13
  • @wichtel just for try remove foreach loop and add $txt = "this is test"; and try again.is generate same o/p – Pratik Boda Oct 18 '16 at 12:14
  • @wichtel problem's is happening to me right now, is there a solution for it? There is no problem with the foreach loop, my string is being perfectly generated inside it but i got only one letter inside the .txt file. – enbermudas Jul 23 '18 at 12:58
  • I discovered that the 'Content-Length" is always 1 o matter how long the string is. Any idea why? – enbermudas Jul 23 '18 at 13:01
1
$images = DB::table('images')->get();
$content = "Names \n";
foreach ($images as $image) {
  $content .= $image->name;
  $content .= "\n";
}

$fileName = "logs.txt";

$headers = [
  'Content-type' => 'text/plain', 
  'Content-Disposition' => sprintf('attachment; filename="%s"', $fileName),
  'Content-Length' => strlen($content)
];   
return Response::make($content, 200, $headers);
use  strlen function in content-length if you use sizeof its always print one char in text document.
thirumal mani L
  • 164
  • 1
  • 5