Cannot start a client-side download after a batch process

Created on 30 January 2024, 7 months ago
Updated 2 February 2024, 7 months ago

Problem/Motivation

I'm creating a contrib Drupal module using the Batch API to create a .zip archive and populate it with some data. The issue is that I cannot find a clean way to start client-side download of the archive once the batch is finished.

Simplified version of my code

I have two main files. The file Form/ExportForm.php that takes some parameters and on submit, calls a service to start the batch and my second main file is Service/BatchService.php. This service handles the whole batch process. Here are the simplified contents of these files:

ExportForm.php

class ExportForm extends FormBase {
    // ...
    public function submitForm(array &$form, FormStateInterface $form_state) {
        $this->batchService->batch_start($form_state->getValue('parameter'));
    }
}

BatchService.php

class BatchService {
    // ...
    public function batch_start($parameter) {
        $archiveName = "archive.zip";
        
        $realpath = \Drupal::service('file_system')->realpath("private://export");
        $zip = new \ZipArchive();
        $zip->open($realpath . '/' . $archiveName);

        // Get data to process
        $data = $this->helperService->getData();

        $operations = [];
        foreach ($data as $element) {
            $operations[] = [
                '...\BatchService::batch_operation_process_element', [$element]
            ];
        }

        $batch = [
            'title' => t('Processing...'),
            'operations' => $operations,
            'finished' => '...\BatchService::batch_operation_finished'
        ];

        batch_set($batch);
    }

    public static function batch_operation_process_element($element, &$context) {
        // Process element and add it to the zip archive.
    }

    public static function batch_operation_finished($success, $results, $operations) {
        if ($success) {
            unlink($results['archivePath']);
        }
    }
}

Already tested solutions

  • I've already tried to put a readfile(...) with the correct headers before removing the temp file in BatchService::batch_operation_finished. This solution works for downloading the archive, once it's downloaded, it's removed and it's fine for this. But this solution creates a new issue. Once the file is downloaded, the user isn't automatically redirected to the previous form page. The user stays on the batch loading page at 100%.
  • I've tried to create a new BinaryFile response in BatchService::batch_operation_finished. This creates exactly the same behaviour as the last test
  • I've tried to do a $form_state->setResponse($binaryResponse); in my Form submit after the batch_start call. This throws an error saying that the requested file in the binary response doesn't exist. To pinpoint this issue, I tried to put a dd('test'); after the call to batch_start and it's executed before the end of the batch process. I think that it's because of the asynchronous behaviour of the Batch API.

Additional information

I searched for issues and solutions but didn't found anything. If someone finds a patch, a solution or another issue answering this question, let me know. Thank you all!

πŸ’¬ Support request
Status

Active

Version

10.2 ✨

Component
BatchΒ  β†’

Last updated 17 days ago

Created by

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Production build 0.71.5 2024