How to Display CSV file content in Controller as an Array? - php

I am trying to display CSV data to array or something as an output using Zend Framework 2
I have created "hello world" module and the controller calls works fine.
CSV File location is data/csv/Foo.csv
Below is my controller:
public function indexAction()
{
$filename = 'data/csv/Foo.csv';
$useFirstRecordAsHeader = true;
$delimiter = ',';
$enclosure = '"';
$escape = '\\';
$this->file = new SplFileObject($filename);
$this->file->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);
$this->file->setCsvControl($delimiter, $enclosure, $escape);
$this->useFirstRecordAsHeader = $useFirstRecordAsHeader;
return $this;
}
But right now I am getting the error:
SplFileObject::__construct(csv/Foo.csv): failed to open stream: No
such file or directory
My CSV file is in the same folder controller/csv/Foo.csv
How to read a CSV file content and display as output array or any other format? I want to do it using Zend Framework 2 only.

You are trying to open $this->file = new SplFileObject('csv/Foo.csv');, since you are using a relative path, on execution time that wont resolve to the folder where your controller is at (it will probably resolve to [yourprojectroot]/csv/Foo.csv).
If you really wanted to store this csv in controller/csv, you should use something like:
$this->file = new SplFileObject(dirname(__FILE__) . '/csv/Foo.csv');
But, saving that csv there is bad for a several reasons. First you'd need to grant write permission to your webserver to be able to write in that directory, and you'd be fundamentally messing up with your data/code structure (data and code shouldn't reside together, but in easily separated silos).
Better, create a folder "data" and and another folder "csv" in your projects directory, give your webserver permission to write there (chmod || chown, other methods), and do something like:
$file = 'data'. DIRECTORY_SEPARATOR . 'csv' . DIRECTORY_SEPARATOR . 'Foo.csv' ;
$this->file = new SplFileObject($file );
Besides that, I'm not sure what you are returning actually makes sense. Try something like:
public function indexAction()
{
$filename = 'data' . DIRECTORY_SEPARATOR . 'csv' . DIRECTORY_SEPARATOR . 'Foo.csv';;
$this->file = new SplFileObject($filename);
$this->file->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);
$this->file->setCsvControl(',', '""', '\\');
$this->useFirstRecordAsHeader = true;
$response = $this->getResponse();
$headers = $response->getHeaders();
$headers->addHeaderLine('Content-Type', 'text/csv');
$contents = $this->file->fread($this->file->getSize());
$response->setContent($contents);
return $response;
}

You should not put your csv directory in the Controller directory, as it is not a controller. It's against the MVC architecture. The good practise is to put data in the data directory, under your root directory, at the same level as your module directory.
So assuming you have it on this data directory, you can simply write:
$this->file = new SplFileObject('data/csv/Foo.csv');
or better (for portability):
$filename = 'data'. DIRECTORY_SEPARATOR . 'csv' . DIRECTORY_SEPARATOR . 'Foo.csv' ;
$this->file = new SplFileObject($filename );

Related

PHP ZipArchive::addGlobe() is slow I can not delete temp directory

I have a code which
Generate csv files to temp directory
Call ZipArchive::addGlob()
Then remove temp dir
But it seems the addGlobe() is not enough fast and files are deleted before addGlob() creates the zip file.
Code looks like:
function saveCsvDaysDataZip($campaign_id)
{
$tempDirName = CampaignsDeviceDays::CSV_REPORTS_DIR . '/' . $campaign_id . '/temp';
$zip = new ZipArchive();
$zip->open($tempDirName . 'reports.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
$addGlob = $zip->addGlob("$tempDirName/*", GLOB_BRACE, ['remove_all_path' => TRUE]);
if( $addGlob === false ) throw new \Exception("CampaignsDeviceDays::saveCsvDaysDataZip() error: \$zip->addGlobe() failed. " . $this->zipArchive->getStatusString() );
}
public function removeCsvExportsDir($campaign_id)
{
$dirName = self::CSV_REPORTS_DIR . '/' . $campaign_id . '/temp';
FileSystem::delete($dirName);
}
$saveCsvDaysDataZip($campaign_id);
$removeCsvExportsDir($campaign_id);
Is it possible that removeCsvExportsDir($campaign_id) is called before addGlob() is done?
Your function had no call to ZipArchive::close - which I guess PHP will then take as "permission" to continue populating the ZIP archive in the background, and return before that process is actually finished.

Laravel 8 image upload: Best practices for storing and editing image files

I need assistance to more understand the concept so I can become a better developer. I want to learn how to refactor the code and erase all duplications.
What's the best practices for image uploads? Renaming them correctly?
I have a block of code that handles two attachments:
if( $request->hasFile('LFImage') ) {
$destination = public_path('app/lostFound/lostItems' . $lostFound->LFImage);
if( File::exists($destination) )
{
File::delete($destination);
}
$file = $request->file('LFImage');
$extension = $file->getClientOriginalExtension();
$filename = $lostFound->LFNumber . '-' . $lostFound->lostItem . '.' . $extension;
$file->move('app/lostFound/lostItems', $filename);
$lostFound->LFImage = $filename;
}
if( $request->hasFile('handoverStatement') ) {
$destination = public_path('app/lostFound/handoverStatements' . $lostFound->handoverStatement);
if( File::exists($destination) )
{
File::delete($destination);
}
$file = $request->file('handoverStatement');
$extension = $file->getClientOriginalExtension();
$filename = $lostFound->lostItem . '-' . $lostFound->LFNumber . '.' . $extension;
$file->move('app/lostFound/handoverStatements', $filename);
$lostFound->handoverStatement = $filename;
}
They're exactly the same except with the upload directory.
How can I make it as a one code block across the entire application with changeable file name and location depending on the form?
Some file names require random strings, how can I "Edit" the random string to the file that was uploaded?
Best practice when uploading and storing files in Laravel is using Storage.
It has all needed methods to work with files, you can save the file like this:
use Illuminate\Support\Facades\Storage;
Storage::put('images/', $request->file('LFImage'));
In the documentation provided above, you can find other examples like renaming and moving files
In order to access these files from web as well, you can use the command php artisan storage:link, which creates a symbolic link to storage folder in your public folder. After you create the symbolic link, you can generate URL to the file like this:
asset('storage/test.txt')
To avoid duplications, you can create a function in your controller to create a file. You will then just call this function with different files to keep the file creation code in one place.
you can simply write this
if ($request->hasFile('logo')) {
deleteImageFromDirectory(setting('logo'), "Settings");
$data['logo'] = uploadImageToDirectory( $request->logo , "Settings");
}
and define uploadImageToDirectory function in your helper functions or create a trait
function uploadImageToDirectory($imageFile, $directory = '' ){
$imageName = $imageFile->getClientOriginalName(); // Set Image name
$imageFile->storeAs("/Images/$directory", $imageName, 'public');
return $imageName;
}

'ZipArchive::close()' not working on web server

I am building a folder with files in it. At the end, I want to Zip this folder. On my local machine using Homestead, everything works correctly.
However, on my web server I am getting the error:
ZipArchive::close(): Can't remove file: No such file or directory
Why? The folder is filled with all files...
My code:
$zip_file = storage_path('app\\takeouts\\takeout_' . $this->dataExports->uuid . '.zip');
$this->zip = new \ZipArchive();
$this->zip->open($zip_file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$this->addAllFilesToZipArchive($this->folder_name);
$this->zip->close();
Storage::deleteDirectory($this->folder_name);
private function addAllFilesToZipArchive($dir)
{
$dirs = Storage::directories($dir);
$files = Storage::files($dir);
foreach ($files as $file)
{
if(Storage::exists(storage_path("app\\" . $file))) {
$this->zip->addFile(storage_path("app\\" . $file), str_replace($this->folder_name,"","/" . $file));
}
}
foreach ($dirs as $dir2) {
$this->addAllFilesToZipArchive($dir2);
}
}
It may seem a little obvious to some but it was an oversight on my behalf.
ll the close() function.
If the files added to the object aren't available at save time the zip file will not be created.

Yii2 - finfo_file(/tmp/phpqE6gyD): failed to open stream: No such file or directory on save after file upload

I am getting following error, when I try to save data into db after file upload:
finfo_file(/tmp/phpqE6gyD): failed to open stream: No such file or directory
This is the code:
$userFolderPath = \Yii::getAlias('#webroot') . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . \Yii::$app->user->getIdentity()->iduser;
$model = new CsFile();
$files = UploadedFile::getInstances($model, 'files');
$errors = [];
if (!file_exists($userFolderPath))
mkdir($userFolderPath, 0777, true);
foreach($files as $file):
$fileModel = new CsFile();
$fileModel->files = $file;
if($fileModel->validate()):
$filename = str_replace(' ', '_', $file->baseName);
if(file_exists($userFolderPath . DIRECTORY_SEPARATOR . $filename . "." . $file->extension)):
$filename .= "-" .uniqid();
endif;
$fileModel->files
->saveAs($userFolderPath .DIRECTORY_SEPARATOR. $filename . '.' . $fileModel->files->extension);
$fileModel->iduser = Yii::$app->user->getIdentity()->iduser;
$fileModel->name = $filename;
$fileModel->extension = $file->extension;
$fileModel->add_date = date('Y-m-d H:i:s');
$fileModel->save();
else:
endif;
endforeach;
var_dump('<pre>', $errors, '</pre>');
I had the same problem a few weeks ago. Turns out, when we rename the file before upload and try to save the model, this error will appear.
If that attribute it's only for handle your upload and have no field in your table, you can just unset this fields before saving: $files Model->files = null.
Let me know if your scenario is different than mine.
Yii2 use UploadFile class through function $model->upload() to save upload file
To fix this use inside your $model->upload function :
return copy($this->YourAttribute->tempName, $newFileName);
instead
return $model->attribute->saveAs($newFileName)
Clyff is right. But in case you are saving the path of the file in database to read later, setting the attribute to null is not going to work.
The problem is when you try to save the model still with result of UploadedFile::getInstance($model, 'file') in the file field which is already used by $model->file->saveAs();
$model->save() cannot save the path of the saved and already removed temporary files path directly.
So after successful $model->file->saveAs($path) you need to do something like:
$model->file = $path;
It was quite unclear to me and spent a bit of time after fileinfo , so hope the answer helps.
I was having same problem, I solved it with this:
$model->file->saveAs($filepath , false)
then...
$model->save(false)
Important: In the saveAs function pass false parameter.
Using false parameter in $model->save(false) that means you are ignoring model validation, which is not right.
But using false as a second parameter in $file->saveAs($path,false) means you are trying to keep the file in the temp folder after being uploaded and allow the model to access the file during validation when trying to save to the database.
If the model fails to access the file (i.e removed from the temp folder after being uploaded), you will getting an ERROR Fail to open a stream, No such file/folder

PHP creating a folder with the right path

<?php
if (isset($_POST['filename']) && isset($_POST['editorpassword']) && isset($_POST['roomname'])) {
$dir = $_POST['filename']; // This must match the "name" of your input
$path = "evo/" . $dir;
if (!file_exists($path)) {
mkdir($path, 0755, true);
}
}
?>
I have this script where I'm trying to create a new folder. The script itself is ran inside of a folder called /evo and by using this code, it creates the folder in there. Where it needs to go is ../../creative however even if I try and use
$path = "./rooms/creative/" . $dir;
or something to that effect it creates it with the base folder as evo so it appears at:
../evo/rooms/creative (creating the folders that don't exist there with it as it should)
I'm just unsure what to write in for the path on where I need it created to find the right location.
Simplest solution is to remove the "evo" in $path = "evo/" . $dir;

Categories