0

I've been banging my head on this one for a few days and thought it was time to ask. The task seems simple - Use PhpSpreadsheet to write an Excel sheet to a Google Cloud Storage bucket.

Currently my thought is that the file will need to streamed to the bucket. The other option it seems would be generating it the file in memory and then writing it to the bucket which I feel like could be resource intensive.

I'm using App Engine with a Standard environment and PHP 7.4.

To test it out out I've been using the most basic code to make the sheet -

require 'vendor/autoload.php';    
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;    
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('A1', 'Hello World !');    
$writer = new Xlsx($spreadsheet);
$writer->save('hello world.xlsx');

Then I'm tying to combine with a basic write to a storage bucket -

require 'vendor/autoload.php';
use Google\Cloud\Storage\StorageClient;

 /**
 * @param string $bucketName The name of your Cloud Storage bucket.
 * @param string $objectName The name of your Cloud Storage object.
 * @param string $source The path to the file to upload.
 */
function upload_object($bucketName, $objectName, $source)
{
    // $bucketName = 'my-bucket';
    // $objectName = 'my-object';
    // $source = '/path/to/your/file';

    $storage = new StorageClient();
    $file = fopen($source, 'r');
    $bucket = $storage->bucket($bucketName);
    $object = $bucket->upload($file, [
        'name' => $objectName
    ]);
}

These are basically the code samples from PhpSpreadsheet and Google. I've gotten both to work on thier own. Does anyone have any ideas about getting the generated Excel sheet to end up on the Cloud Storage bucket?

// Added 2022-01-13 //////////////////////////////

After a few comments I've tried different options and have listed the results below. I think it is getting close, but not quite there.

<?php
require 'vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Google\Cloud\Storage\StorageClient;

$bucketName = 'BUCKETNAME'; // Name of the GCS bucket
$objectName = 'new_file_name.xlsx'; // Name of the new file we are creating
$objectLocation = 'gs://'.$bucketName.'/'.$objectName; // Path of the new file

try {

    $storage = new StorageClient();
    $storage->registerStreamWrapper();

    $spreadsheet = new Spreadsheet();
    $sheet = $spreadsheet->getActiveSheet();
    $sheet->setCellValue('A1', 'Hello World !');
    $sheet->setCellValue('B1', 'Hello You !');

    $writer = new Xlsx($spreadsheet);
    
    // Trial 1
    // This displays "Could not open file "gs://BUCKETNAME/new_file_name.xlsx" for writing." in the browser window.
    $writer->save($objectLocation);

    // Trial 2
    // This creates an empty file in the GCS bucket and displays the XLSX file code on the screen
    //$file = fopen($writer->save('php://output'), 'r');
    //$bucket = $storage->bucket($bucketName);
    //$object = $bucket->upload($file, [
    //    'name' => $objectName
    //]);

    // Trial 3
    // This displays "Could not open file "new_file_name.xlsx" for writing." in the browser window.
    //$file = fopen($writer->save($objectName), 'r');
    //$bucket = $storage->bucket($bucketName);
    //$object = $bucket->upload($file, [
    //    'name' => $objectName
    //]);
}
catch (Exception $e) {
    echo $e->getMessage();
}
fitz
  • 69
  • 1
  • 6
  • 1) What is the problem with using the code that you wrote? 2) Both Google Cloud Storage and PhpSpreadsheet support **streams**. That means you do not need to create a file before writing to Cloud Storage. Refer to #3 at this link https://cloud.google.com/appengine/docs/standard/php7/using-cloud-storage#using_cloud_storage_with_app_engine – John Hanley Jan 13 '22 at 04:06
  • Hi @fitz, According to this [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/recipes/), have you tried using `$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx'); $writer->save('php://output');` to store the file temporarily and use it to upload in Google Cloud Storage without saving the file locally? – Marc Anthony B Jan 13 '22 at 09:45
  • John - using the built in GCS streaming seems like the best option, but I haven't been able to get it to work. See Trial 1 in the code I added to the post. Marc - I thought I could maybe capture the result of "php://output" and direct in to the new file, but it doesn't seem to work right. See Trial 2 in the code I added to the post. Thanks for the help! – fitz Jan 13 '22 at 15:36
  • You are not using PHP Streams correctly. Google search for examples. – John Hanley Jan 13 '22 at 19:28

1 Answers1

1

Here's a working sample code:

<?php
require 'vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Google\Cloud\Storage\StorageClient;

$bucketName = 'BUCKET_NAME'; // Name of the GCS bucket
$objectName = 'FILENAME.xlsx'; // Name of the new file we are creating

try {
    $storage = new StorageClient();
    $storage->registerStreamWrapper();

    $spreadsheet = new Spreadsheet();
    $sheet = $spreadsheet->getActiveSheet();
    $sheet->setCellValue('A1', 'Hello World !');
    $sheet->setCellValue('B1', 'Hello You !');

    $writer = new Xlsx($spreadsheet);

    ob_start();
    $writer->save('php://output');
    $content = ob_get_contents();
    ob_end_clean();

    $bucket = $storage->bucket($bucketName);
    $object = $bucket->upload($content, [
        'name' => $objectName
    ]);
}
catch (Exception $e) {
    echo $e->getMessage();
}
?>

Below are screenshots of the spreadsheets with content in the Google Cloud Storage: enter image description here

enter image description here

In PHP, there is a reference called PHP Output Control.

I used ob_start() to create an output buffer and ob_end_clean() to delete the topmost output buffer and all of its contents without sending anything to the browser.

I also used php://output to store the file temporarily in the script's working directory according to the PHPSpreadsheet Documentation.

Marc Anthony B
  • 3,635
  • 2
  • 4
  • 19
  • Marc, great reply. I'm traveling right now and won't be able to try it for a few days. I'm wondering how this affects memory for larger sheets. – fitz Jan 18 '22 at 11:43
  • Hi @fitz, You could check out this [thread](https://stackoverflow.com/a/31412160/16895640) for more information about memory consumption of `PHP Output Buffering Control`. and also checkout this [documentation](https://www.php.net/manual/en/book.outcontrol.php) for more Output Buffering Control functions that you could use. – Marc Anthony B Jan 19 '22 at 09:19
  • Marc, I tested this out today and it wrote to Cloud storage with no issue. When I generated a large sheet my instance ran out of memory and errored out. I took a look at the links you sent. I think streaming is the way to do it. Seems like it should be a simple as [this S3 example](https://github.com/PHPOffice/PhpSpreadsheet/issues/2249) except using "gs" like [here](https://github.com/googleapis/google-cloud-php-storage). – fitz Jan 20 '22 at 17:02