3

This is my javascript request:

var XHR = new XMLHttpRequest();
XHR.responseType = 'blob';
XHR.addEventListener('readystatechange', function() {
    if (XHR.readyState == 4) {
        if (XHR.status == 200) {
            var blob = new Blob([XHR.response], {type: "application/mp3"});
            var url = window.URL.createObjectURL(blob);
            var a = document.createElement('a');
            a.href = url;
            a.download = 'Myfile.mp3';
            a.click();
            window.URL.revokeObjectURL(url);
        } else {
            document.getElementById(regidrs).innerHTML = "Error: " + XHR.responseText;
        }
    }
});

var FD  = new FormData();
FD.append('uuid', uuid);
XHR.open('POST', '/fetch');
XHR.send(FD);

This is my module routing:

my_module/my_module.routing.yml

my_module.content:
  path: '/fetch'
  defaults:
    _controller: '\Drupal\my_module\Controller\MyController::content'
    _title: 'Hello World'
  requirements:
    _permission: 'access content'

And my controller:

<?php

namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Form\drupal_set_message;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Defines HelloController class.
 */
class MyController extends ControllerBase {

  /**
   * Display the markup.
   *
   * @return array
   *   Return markup array.
   */
  public function content() {
     ...
        $response= file_get_contents(....);
        $file = file_save_data($response, "public://myaudiofile.mp3", FILE_EXISTS_REPLACE);
     ...
     return [
      '#type' => 'markup',
      '#markup' => $response,
    ];
  }

}

But file_get_contents returns a mp3 file, and I need to pass this mp3 to the javascript httpresponse in order to donwload it. Saving $response to a file, I have checked the file is ok.

How can I do it?

  • 1
    Once the question gets answers, its sense cannot be changed, nor can more details be added. In both the cases, the given answers would be invalidated. Questions aren't for an extended back and forth between the user who ask the question and the users who answer. It doesn't work as with forums. – kiamlaluno Oct 6 '18 at 21:16
  • Ok. I'm sorry. I thought It was the right way to show my changes. – briast Oct 6 '18 at 21:58
5

With the BinaryFileResponse class, you can return the content of a file in the response. The FileDownloadController class is an example of controller returning the content of a file. It's the controller for /system/files/{scheme}, /system/files/{filepath}, and /system/temporary.
See its FileDownloadController::download() method.

public function download(Request $request, $scheme = 'private') {
  $target = $request->query
    ->get('file');

  // Merge remaining path arguments into relative file path.
  $uri = $scheme . '://' . $target;
  if (file_stream_wrapper_valid_scheme($scheme) && file_exists($uri)) {

    // Let other modules provide headers and controls access to the file.
    $headers = $this
      ->moduleHandler()
      ->invokeAll('file_download', [
      $uri,
    ]);
    foreach ($headers as $result) {
      if ($result == -1) {
        throw new AccessDeniedHttpException();
      }
    }
    if (count($headers)) {

      // \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond()
      // sets response as not cacheable if the Cache-Control header is not
      // already modified. We pass in FALSE for non-private schemes for the
      // $public parameter to make sure we don't change the headers.
      return new BinaryFileResponse($uri, 200, $headers, $scheme !== 'private');
    }
    throw new AccessDeniedHttpException();
  }
  throw new NotFoundHttpException();
}

The ExportForm class is another example of how to return a file, even if it's a form class. What changes is how the response is used, but how the BinaryFileResponse class is used is still relevant for a controller.
The relevant code is the following one, in ExportForm::submitForm().

$response = new BinaryFileResponse($uri);
$response->setContentDisposition('attachment', $filename);
  • 1
    This is the proper way. – Kevin Oct 6 '18 at 19:49
  • Thanks @kiamlaluno. I have tried to rewrite my code with your suggestions, but without success. I have edited the question in order to show you my changes. – briast Oct 6 '18 at 20:08
  • @briast You just need to return an object of the BinaryFileResponse class. Which hooks the first controller invokes is a different matter, since it is the controller for private files. – kiamlaluno Oct 6 '18 at 21:23
  • Sorry, but I don't know how to do it. I have tried it in several ways without success. Would you mind if you put me an example. Thanks. – briast Oct 7 '18 at 8:14
  • At least I found the way. The problem was in javascript side.Now it works. I would like to put the solution, but I'm not sure if I should add it as a new answer or update my question. Thanks. – briast Oct 8 '18 at 6:41
0

You are 99% there, just return a redirect to the file:

class MyController extends ControllerBase {

  public function content() {
     ...
        $response = file_get_contents(....);
        $file = file_save_data($mp3, "public://myaudiofile.mp3", FILE_EXISTS_REPLACE);
     ...
    return new RedirectResponse( file_create_url('public://myaudiofile.mp3'));

  }

}
  • 1
    Thanks for your quick answer but It doesn't work. On javascript event, I get XHR.status error 500. In Drupal log I see the next error: Recoverable fatal error: Object of class Drupal\Core\Url could not be converted to string en Drupal\Component\Utility\UrlHelper::isExternal() . – briast Oct 6 '18 at 14:20
  • I just corrected it. can you re-try, please? – Cesar Moore Oct 6 '18 at 14:31
  • Briast. Can you try this method. api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Url.php/… – Alen Simonyan Oct 6 '18 at 14:33
  • Cesar, I have changed it but now the error is 404. – briast Oct 6 '18 at 19:51
  • 404 would mean that the file is not there. Can you verify whether the file is really saved? – Cesar Moore Oct 6 '18 at 20:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.