Reputation: 752
Found a handful of questions on here about this with no answer, so hopefully, someone can point me in the right direction...
I'm trying to create and save a csv file to storage, then update the DB in Laravel. I can create the file successfully, and I can update the DB successfully... but I'm stuck on putting them both together. In my controller, I have this for creating the file (taken from here):
public function updatePaymentConfirm(Request $request) {
$users = User::all();
$fileName = 'test.csv';
$headers = array(
"Content-type" => "text/csv",
"Content-Disposition" => "attachment; filename=$fileName",
"Pragma" => "no-cache",
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
"Expires" => "0"
);
$columns = array('First Name', 'Email');
$callback = function() use($users, $columns) {
$file = fopen('php://output', 'w');
fputcsv($file, $columns);
foreach ($users as $user) {
$row['First Name'] = $user->first_name;
$row['Email'] = $user->email;
fputcsv($file, array($row['First Name'], $row['Email']));
}
fclose($file);
};
// return response()->stream($callback, 200, $headers);
}
When the function completes, the last line (that's commented out) prompts the user to download the newly created file (which is not the functionality I'm looking for). I tried adding this to my controller in its place for saving to storage and also updating the database:
$fileModel = new UserDocument;
if($callback) {
$filePath = $callback->storeAs('uploads', $fileName, 'public');
$fileModel->name = $fileName;
$fileModel->file_path = '/storage/' . $filePath;
$fileModel->save();
return back()
->with('success','File has been uploaded.')
->with('file', $fileName);
}
It saves a row to the db, albeit incorrectly, but it doesn't save the file to storage. I've reworked the $filePath
line a million times, but I keep getting this error Call to a member function storeAs() on resource
or something similar. I'm relatively new to working with Laravel, so I'm not sure what I should be looking for. Thoughts?
Upvotes: 9
Views: 26768
Reputation: 19
This code is easy and functional, use Laravel Storage Class https://laravel.com/docs/9.x/filesystem#main-content
use Illuminate\Support\Facades\Storage;
// data array
$results = [
['id' => 0, 'name' => 'David', 'parent' => 1],
['id' => 1, 'name' => 'Ron', 'parent' => 0],
['id' => 2, 'name' => 'Mark', 'parent' => 1]
];
// create a variable to store data
$pages = "id,name,parent\n"; // use " not ' or \n not working
// use foreach to data
foreach ($results as $where) {
$pages .= "{$where['id']},{$where['name']},{$where['parent']}\n";
}
// use Fecades Laravel Storage
Storage::disk('local')->put('file.csv', $pages);
Upvotes: 1
Reputation: 14747
Today I have fixed it with this snipe:
// output up to 5MB is kept in memory, if it becomes bigger it will
// automatically be written to a temporary file
$csv = fopen('php://temp/maxmemory:'. (5*1024*1024), 'r+');
fputcsv($csv, array('blah','blah'));
rewind($csv);
$output = stream_get_contents($csv);
// Put the content directly in file into the disk
Storage::disk('myDisk')->put("report.csv", $output);
Upvotes: 2
Reputation: 752
Removed everything and started over... got it! And for anyone else running into the same issue: just calling for a file that doesn't exist creates the file (unless the file exists - then it updates it), so you don't have to create a temp file or use $file = fopen('php://output', 'w');
to create the file. It'll automatically "save" the newly generated file in the file path you specified when you fclose()
out of the file.
The only thing I'll note is that the file path has to exist (the file doesn't, but the file path does). In my instance, the file path already exists, but if yours doesn't or if you're not sure if it does, check to see if it exists, and then make the directory.
public function updatePaymentConfirm(Request $request) {
$user = Auth::user();
$path = storage_path('app/public/docs/user_docs/'.$user->id);
$fileName = $user->ein.'.csv';
$file = fopen($path.$fileName, 'w');
$columns = array('First Name', 'Email Address');
fputcsv($file, $columns);
$data = [
'First Name' => $user->first_name,
'Email Address' => $user->email,
];
fputcsv($file, $data);
fclose($file);
$symlink = 'public/docs/user_docs/'.$user->id.'/';
$fileModel = new UserDocument;
$fileModel->name = 'csv';
$fileModel->file_path = $symlink.$fileName;
$fileModel->save();
return redirect()->route('completed');
}
Everything worked perfectly locally, and when I pushed this to production, I received this error 🙄:
fopen(https://..../12-3456789.csv): failed to open stream: HTTP wrapper does not support writeable connections.
I'm saving to an s3 bucket, and I had to rework the entire process. You can't create and/or write to a file in the directory. I had to create a temp file first. Here's where I landed:
$user = Auth::user();
$s3 = Storage::disk('s3');
$storage = Storage::disk('s3')->url('/');
$path = 'public/docs/user_docs/'.$user->id.'/';
$csvFile = tmpfile();
$csvPath = stream_get_meta_data($csvFile)['uri'];
$fd = fopen($csvPath, 'w');
$columns = array('First Name', 'Email Address');
$data = array(
'First Name' => $user->first_name,
'Email Address' => $user->email,
);
fputcsv($fd, $columns);
fputcsv($fd, $data);
fclose($fd);
$s3->putFileAs('', $csvPath, $path.$user->ein.'.csv');
Upvotes: 11