HardDrive's avatar

Import CSV files and Excel using file streamed from S3

I have a job to process large uploaded csv files. I want to process these as a queued job. Because the queued jobs can not accept a file as a passed in parameter I had the idea of uploading the file to S3 first then use the file from s3 in my csv processing job.

I have tried this using Maatwebsite/Laravel-Excel but it wont accept the streamed file from S3.

$contents = Storage::disk('s3')->get($filename);
        $results = Excel::load($contents);

So I can process the csv in the background can anyone recommend a better way of doing this or maybe a better csv, excel import library.

0 likes
12 replies
D9705996's avatar
D9705996
Best Answer
Level 51

Because the queued jobs can not accept a file as a passed in parameter

You pass what you like to your job through the constructor

class ProcessCsv implements ShouldQueue
{
    protected $file;

    public function __construct($file)
    {
        $this->file = $file;
    }

    public function handle() 
    {
        // do your processing here
       $results = Excel::load($this->file);       
    }
}
...

// Dispatch your job in your controller, model, etc
ProcessCsv::dispatch($file);
1 like
HardDrive's avatar

Actually I tried this and didn't have any luck I received the error

Serialization of 'Illuminate\Http\UploadedFile' is not allowed

I also tried to comment out the Serialization parts and received the same error

amespace App\Jobs;

use Illuminate\Bus\Queueable;
//use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class ProcessUploadedCDRFile implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable;//, SerializesModels;

    protected $file;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($file)
    {
        $this->file = $file;
    }
D9705996's avatar

@RossUK - I would pass in the filename rather than the file itself, then load it in the job. Example from your original code but you could probably just right to local storage.

    public function handle() 
    {
       $contents = Storage::disk('s3')->get($this->file);
       $results = Excel::load($contents);       
    }
1 like
HardDrive's avatar

That's what I was thinking. I just need to work out the best way of managing the files so they are deleted after they are processed :)

Thanks for your help

D9705996's avatar

Just use use the storage facade

Storage::delete($this->file);
Cronix's avatar

I just need to work out the best way of managing the files so they are deleted after they are processed

$contents = Storage::disk('s3')->get($this->file);
$results = Excel::load($contents); // file is now loaded in results, so don't need it
Storage::disk('s3')->delete($this->file);

?

HardDrive's avatar

Thanks almost there something isn't quite right when I try this I get the following

Could not open /Users/Ross/Projects/TestCDRProcess/Id , Customer, Telephone Number,

Those are the file headers ?

HardDrive's avatar

I think its because the PHPExcel_Reader is expecting a filename but I'm not sure how to sort it.

below extracted from error message

  public function canRead($pFilename)
    {
        // Check if file exists
        if (!file_exists($pFilename)) {
            throw new PHPExcel_Reader_Exception("Could not open " . $pFilename . " for reading! File does not exist.");
        }
Cronix's avatar

Ah, that will be a problem. file_exists() will only work on a local filesystem, not something remote. You might have to copy the file from S3 to your localhost, then add it to excel, then delete it from both places.

HardDrive's avatar

Think I'm going to go with local storage as it's going to be deleted after use :)

Ill post back my solution once I have read how to store and delete off the local file system to a temp folder

HardDrive's avatar

Got it sorted using local storage thanks. Is it normal to have to prefix the path (in this case storage/app/) ?

here is what I ended up with

Controller Method:

public function store(Request $request)
    {

        if($request->hasFile('file')) 
        {
        $path = $request->file('file')->store('temp-files');
        ProcessUploadedCDRFile::dispatch($path);
        return back();
        } 
    }

and the job method

public function handle()
    {

       $results = Excel::load('storage/app/' . $this->path); 
       Storage::delete('storage/app/' . $this->path);

       // do something useful with results
    }

Thanks again

Please or to participate in this conversation.