Transactions for file operations in Laravel - laravel

In Laravel I can do database transactions by passing a closure to the DB::transaction function.
Does Laravel have any support for a similar feature for the File or Storage facade? Where the file operations are run in a transaction, with rollback in case a file operation fails?
I'm imagining something like
$files = ... // Something that returns a collection
File::transaction(function () use ($files) {
$files->each(function() {
File::move(....);
});
});

There is no built in way of doing it so you'd have to make an implementation yourself.
A simple method of achieving it would be
$fileName = ""; // or $fileNames ( array ) if multiple file uploads
$files = "" // to be used if you're going to update or delete files. Again if multiple file modifications then use array
try{
/* Just a note, but your new file could overwrite any existing files,
so before uploading, check if another file exists with same filename
And if it does, load that file and keep it in the $files variable
*/
// Upload File
$fileName = // name of uploaded file
$files = // Any existing file you're going to modify. Load the entire file data, not just the name
// Modify/Delete a file
}( \Exception $e ){
// Now delete the file using fileName or $fileNames if the variable is not empty
// If you modified/deleted any file, then undo those modifications using the data in $files ( if it's not empty )
}
In this method, existing files are loaded to memory, but if there are multiple large files, it might be better to move them to a temporary location instead, and move them back if any exception is thrown. Just don't forget to delete these temporary files if the file transaction is a success

Related

How to get file as a resource with Storage?

I am trying to lock a file with the flock function but I am not able to do it. I work with Laravel 8 and Storage Class.
The code is as follows:
$disk = Storage::disk('communication');
$file_name = 'received.json';
$file_exists = $disk->exists($file_name);
if($file_exists){
flock($disk->get($file_name), LOCK_EX);
...
}
The problem I'm having is that when I invoke the get() function on the file path, it returns the contents of the file (a string), which causes the following error:
flock() expects parameter 1 to be resource, string given
I need to know how to get file as a resource and not the content of the file.
Could someone help me and tell me how to do it?
Thank you very much in advance.
You can use Storage::readStream() method
if($file_exists){
$stream=Storage::disk('communication')->readStream($file_name);
flock($stream, LOCK_EX);
}
As per php doc
flock(resource $stream, int $operation, int &$would_block = null): bool
First param needed stream.flock() allows you to perform a simple
reader/writer model which can be used on virtually every platform
(including most Unix derivatives and even Windows).
Ref:https://www.php.net/manual/en/function.flock.php

Laravel Multiple Upload File

How to show uploaded files using multiple upload files?
With data like this
Lets consider you name is in this variable = $uploadedFiles;
$uploadedFiles = ["file1","file2","file3"];
$temp = json_decode($uploadedFiles);
foreach($temp as $name) {
echo $name;
}
It will print your file name 1 by 1
It just names you need the files also
but if you want to store only that names then you can do it by taking a loop for that variable array
if it is not an array then convert to array using json_decode(array)

Generate Excel file with PhpSpreadsheet in Codeigniter from a view

I am trying to generate an Excel file with CodeIgniter and the PhpSpreadsheet library from a view. The reports that I need to make are not a list but much more complex and I can generate them more quickly with a view and sending parameters. This is my code:
$data = $this->model->bringdata();
$view = $this->load->view("data_view", $ data);
$spreadsheet = new Spreadsheet();
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$writer-> save($view);
And the following error is shown
Severity: Warning
Message: fopen (): Filename can not be empty
Filename: Writer / Html.php
Line Number: 160
In the PhpSpreadsheet documentation it shows that an html page must be explicitly sent:
$writer->save("05featuredemo.htm");
Is there any way to do what I need?
Thanks for the help
It's a simple solution, you need to pass true to the third argument of load->view in order for it to return a string rather than sending it to the browser.
$view = $this->load->view("data_view", $ data, true);
Added after comment
I made the mistake of not looking into how the Html writer works.
The HTML writer writes a spreadsheet as an html file. It does not create a spreadsheet. As I understand it (and I might be mistaken) you can use \PhpOffice\PhpSpreadsheet\Reader\Html to read an html file into a spreadsheet.
In your case I think you will have to save the string returned from $this->load->view() to a temporary file. CodeIgniter's file_helper might be useful here.
You then read the temporary file to create a spreadsheet. After that you can use $writer to save the spreadsheet somewhere as any of the file types the PhpSpreadsheet supports.
$view = $this->load->view("data_view", $data, true);
$this->load->helper("file");
$fileName = "temp_file_name.html";
$path = "full/path/to/some_writable_folder/";
$path_file = $path . $fileName;
if (write_file($path_file, $view))
{
//create spreadsheet the temp html
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Html();
$spreadsheet = $reader->load($path_file);
//write out to html file
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$writer->save($path."05featuredemo.htm");
//delete the temporary file
unlink($path_file);
}
else
{
//handle the failure to write the temp file
}
I haven't tested this and I'm not sure you really want to save to an html file, but I think the above is close to the answer.
Second addition
If you want to save .xlsx files you need an Xlsx writer. Use the following instead of what's shown above.
//write out to excel file
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save($ path. "doc.xlsx");
I confess that I have never used, or considered using the html reader so I'm not sure you'll get what you want. I honestly think that creating an html table so you can import it into excel is probably doing it the hard way.

Files not sent to server: Illegal invocation at File.remoteFunction

I have a function that preprocess a complex object before sending it to server to save some space, so it creates a copy of the object, and got this error on submission (AJAX).
It was working before I decided to create a "clean" copy of the object.
Why is this error thrown?
Found out that you can´t copy the file object, the spec doesn´t allow it, so, in my function, I had to make every file point to the original file object in order to accomplish the submission of every file:
prepareData = function(originalObject){
var data = clone originalObject;
data.id_bs = data.bs.id;
delete data.bs;
data.id_Cc= data.cc.id;
delete data.cc;
//Added this to make it work
for ( var kDoc = 0; kDoc < originalObject.docs.length; kDoc++ ){
data.docs[kDoc] = originalObject.docs[kDoc];
}
return data;
}

Accessing temporary file from upload in django view

Just as the title says, I want to know how to access the data from the temporary file stored by Django, when a file is uploaded, inside a view.
I want to read the data uploaded values so I can make a progress bar. My methodology is to perform a jquery getJSON request:
function update_progress_info() {
$progress.show();
$.getJSON(progress_url, function(data, status){
if (data) {
var progress = parseInt(data.uploaded) / parseInt(data.length);
var width = $progress.find('.progress-container').width()
var progress_width = width * progress;
$progress.find('.progress-bar').width(progress_width);
$progress.find('.progress-info').text('uploading ' + parseInt(progress*100) + '%');
}
window.setTimeout(update_progress_info, freq);
});
};
where progress_url is the view I have that handles the uploaded file data:
# views.py (I don't know what to do here):
def upload_progress(request):
for line in UploadedFile.temporary_file_path
response = (line)
return response
Django handles uploaded files with UploadHandler defined in settings.py with this name FILE_UPLOAD_HANDLERS that defaults to this tuple:
FILE_UPLOAD_HANDLERS =
("django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",)
The behavior with file uploads is that if the file is less than 2.5 mg then it will be kept on memory, hence, they will not be written in disk as temporary files.
If the file weights more, it will be written in chunks in the FILE_UPLOAD_TEMP_DIR in the settings.py. That's the file you'll have to query to know how many bytes have been uploaded.
You can access the uploaded/uploading files through your request variables in views like this: file = requests.FILES['file'] . There, file variable will have the type UploadedFile which contains a method temporary_file_path with the address of the file in the disk being uploaded. (Note: only files larger than 2.5 mg will have this methods) so there you may get the size of the file being uploaded.
Another way to do this is create your own UploadHandler like a ProgressBarUploadHandler and add it to your file upload handlers. This is the way the docs recommend it. Here are some snippets and tutorials for doing it.
If you need any more info the doc is really well documented.
I hope you find this helpful. Good luck.

Resources