Change save folder in burzum/cakephp-file-storage plugin - php

I'm using a plugin to save files in CakePHP 3: burzum/cakephp-file-storage,
everything works perfectly
but now I need to change the directory where file is being saved dynamically (through the directory where
the file is saved by parameter for example), how can I do this?
Path actual:
[htdocs]\[AppName]\tmp[FileExtension.png]
It is possibly stay with the following structure for example:
[htdocs]\[AppName-Images]\Products[FileExtension.png]
PHP Code:
public function saveFileLFS($stringSeparator, $storeName, $productName)
{
$key = $storeName . $stringSeparator . $productName . $stringSeparator .
$this->request->data['Media']['file']['name'];
if(StorageManager::adapter('Local')->write($key,
file_get_contents($this->request->data['Media']['file']['tmp_name']))){
return true;
}else
{
return false;
}
}
Github Link

The reason it ends up in TMP is that this is the default configuration that comes with the plugin. It goes to TMP because this is the only place besides logs that should be writeable in a proper application setup and the plugin should work out of the box. Maybe I'll change this in a future release (4.0) so that you have to configure it to use it, to make people aware of it.
For the local adapter change it like this:
StorageManager::config('Local', [
'adapterOptions' => [ROOT . DS . 'file_storage' . DS], // Your base path here
'adapterClass' => '\Gaufrette\Adapter\Local',
'class' => '\Gaufrette\Filesystem'
]);
This will override the default. Actually this is already documented and explained here.
I recommend you to take a look at the 1.1.0 branch (currently release is 1.1.0-RC1). Some people already use it and I'm happy to get feedback about it. The whole way paths and file names are generated has been completely rewritten and abstracted into a set of classes called "path builders".

Related

Laravel 6 Storage results in a 404 error when trying to fetch files

I have tried to setup an upload script in Laravel and have followed the instructions in the docs.
I created a Symlink using the Laravel script and it looks like the following
storage -> /Users/username/Sites/switch/storage/app/public
The problem arrives when I go to upload the image and then get result of the image url in return. As you can see to match the symlink I set the folder to be public below.
$path = $request->file('manufacturer_image_name')->store('public');
echo asset($path);
and this returns
http://127.0.0.1:8000/public/XxIX7L75cLZ7cf2xzejc3E6STrcjfeeu3AQcSKz1.png
the problem is this doesn't work and throws a 404 but if I manually change the url from "public" to "storage" it will find the image.
http://127.0.0.1:8000/storage/XxIX7L75cLZ7cf2xzejc3E6STrcjfeeu3AQcSKz1.png
Shouldn't
echo asset($path);
be returning a url containing storage instead of public?
assett($path) is for generating a URL for assets that are just in the public folder, things like the Mix generated CSS and JS files. If you user Laravel Storage to save the file, you also have to use Laravel storage to generate the file URL.
Storage::url('file.jpg');
Well, there are a lot of ways to do that, pick anyone which fits you best.
// using storage_path helper
storage_path('public/' . $filename);
// you could make a double-check with File::exist() method
$path = storage_path('public/' . $filename);
if (!File::exists($path)) {
abort(404);
}
// using asset helper
asset('storage/your_folder/image.png');
// using url helper
url('storage/your_folder/image.png');
// using Storage facade
Storage::url($photoLink)
Here is the simplest and exact thing for your issue
if(!empty($request->file('manufacturer_image_name'))){
$path = storage_path('public/image/');
$image_path = Storage::disk('public')->put('manufacturer_image_name', $request->file('manufacturer_image_name'));
//Assuming you have a model called Manufacturer and created $manufacturer = new Manufacturer()
$manufacturer->manufacturer_image_name = isset($image_path) ? "storage/".$image_path : "";
}
Thanks for the help, I discovered this answer the fits nearly perfectly what I am after. Laravel: Storage not putting file inside public folder
This was what I ended up with.
if($request->file('manufacturer_image_name')){
$path = Storage::disk('public')->put('logo', $request->file('manufacturer_image_name'));
echo $path;
}
$path now returns "logo/filename.ext" instead of "public/ or storage/" so I can store this directly in the db.

Create .tar.gz file using PHP

The project I am working on requires creating .tar.gz archives and feeding it to an external service. This external service works only with .tar.gz so another type archive is out of question. The server where the code I am working on will execute does not allow access to system calls. So system, exec, backticks etc. are no bueno. Which means I have to rely on pure PHP implementation to create .tar.gz files.
Having done a bit of research, it seems that PharData will be helpful to achieve the result. However I have hit a wall with it and need some guidance.
Consider the following folder layout:
parent folder
- child folder 1
- child folder 2
- file1
- file2
I am using the below code snippet to create the .tar.gz archive which does the trick but there's a minor issue with the end result, it doesn't contain the parent folder, but everything within it.
$pd = new PharData('archive.tar');
$dir = realpath("parent-folder");
$pd->buildFromDirectory($dir);
$pd->compress(Phar::GZ);
unset( $pd );
unlink('archive.tar');
When the archive is created it must contain the exact folder layout mentioned above. Using the above mentioned code snippet, the archive contains everything except the parent folder which is a deal breaker for the external service:
- child folder 1
- child folder 2
- file1
- file2
The description of buildFromDirectory does mention the following so it not containing the parent folder in the archive is understandable:
Construct a tar/zip archive from the files within a directory.
I have also tried using buildFromIterator but the end result with it also the same, i.e the parent folder isn't included in the archive. I was able to get the desired result using addFile but this is painfully slow.
Having done a bit more research I found the following library : https://github.com/alchemy-fr/Zippy . But this requires composer support which isn't available on the server. I'd appreciate if someone could guide me in achieving the end result. I am also open to using some other methods or library so long as its pure PHP implementation and doesn't require any external dependencies. Not sure if it helps but the server where the code will get executed has PHP 5.6
Use the parent of "parent-folder" as the base for Phar::buildFromDirectory() and use its second parameter to limit the results only to "parent-folder", e.g.:
$parent = dirname("parent-folder");
$pd->buildFromDirectory($parent, '#^'.preg_quote("$parent/parent-folder/", "#").'#');
$pd->compress(Phar::GZ);
I ended up having to do this, and as this question is the first result on google for the problem here's the optimal way to do this, without using a regexp (which does not scale well if you want to extract one directory from a directory that contains many others).
function buildFiles($folder, $dir, $retarr = []) {
$i = new DirectoryIterator("$folder/$dir");
foreach ($i as $d) {
if ($d->isDot()) {
continue;
}
if ($d->isDir()) {
$newdir = "$dir/" . basename($d->getPathname());
$retarr = buildFiles($folder, $newdir, $retarr);
} else {
$dest = "$dir/" . $d->getFilename();
$retarr[$dest] = $d->getPathname();
}
}
return $retarr;
}
$out = "/tmp/file.tar";
$sourcedir = "/data/folder";
$subfolder = "folder2";
$p = new PharData($out);
$filemap = buildFiles($sourcedir, $subfolder);
$iterator = new ArrayIterator($filemap);
$p->buildFromIterator($iterator);
$p->compress(\Phar::GZ);
unlink($out); // $out.gz has been created, remove the original .tar
This allows you to pick /data/folder/folder2 from /data/folder, even if /data/folder contains several million OTHER folders. It then creates a tar.gz with the contents all being prepended with the folder name.

File not found at path when trying to copy

I'm trying to copy a file from one location to another. I'm pretty sure the location is correct, but I'm still getting the error in the title.
Here's some code:
$oDirectory = new \RecursiveDirectoryIterator($extractFolder.'/res');
$oIterator = new \RecursiveIteratorIterator($oDirectory);
foreach($oIterator as $oFile) {
if ($oFile->getFilename() == 'icon.png') {
$icons[filesize($oFile->getPath().'/icon.png')] = $oFile->getPath().'/icon.png';
}
}
asort($icons);
print_r($icons);
$icon_source = end($icons);
echo $icon_source;
$generated_icon_file = str_slug($packagename.$version).'.png';
Storage::copy($icon_source, $generated_icon_file);
The print_r returns (which means the files exist):
Array ( [19950] => /var/www/apk.land/storage/extracted_apks/res/drawable-xxhdpi-v4/icon.png [31791] => /var/www/apk.land/storage/extracted_apks/res/drawable-xxxhdpi-v4/icon.png [6979] => /var/www/apk.land/storage/extracted_apks/res/drawable-hdpi-v4/icon.png [10954] => /var/www/apk.land/storage/extracted_apks/res/drawable-xhdpi-v4/icon.png )
The echo returns:
/var/www/apk.land/storage/extracted_apks/res/drawable-xxxhdpi-v4/icon.png
And the exact error is:
File not found at path:
var/www/apk.land/storage/extracted_apks/res/drawable-xxxhdpi-v4/icon.png
P.S. PHP's copy function works just great.
I can't find the problem here.
Any suggestions?
You said that the error is like this:
File not found at path: var/www/apk.land/storage/extracted_apks/res/drawable-xxxhdpi-v4/icon.png
Here error is saying it cannot find at var/www that means it's looking for apk.land/var/www whereas your file is located somewhere at /var/www. A quick fix to this can be use file protocol. Just use it like:
file:///var/www/storage/apk.land/storage/extracted_apks/res/drawable-xxxhdpi-v4/icon.png
Try with File::copy($file, $dest) instead of Storage::copy($old, $new)
File::copy() is the wrapper on PHP's copy() function
First of all, if you are using Laravel's Storage Facade and with that the underlying Flysystem you have to know, that it is not intended to work with absolute paths like you did. The benefit of that is, that you could potentially work with different storage disks, that all have own configurations, that can be set in your config/filesystems.php file.
Assuming you did not change anythig there, the default "disk" would be local with an root of storage_path('app') ( = the path to your laravel storage folder/app )
If you want to know, why it is failing, we have to take a look at the source code you will find in the following code in the file vendor\league\flysystem\src\Filesystem.php
public function copy($path, $newpath)
{
$path = Util::normalizePath($path); // <-- this will strip leading /
$newpath = Util::normalizePath($newpath);
$this->assertPresent($path); // this will cause the error in your case, because assertion cannot be fullfilled in case of missing leading /
$this->assertAbsent($newpath);
return $this->getAdapter()->copy($path, $newpath); // calls the copy of your Adapter, assuming local in your case
}
So have a look, what would go on, if $this->getAdapter()->copy($path, $newpath) was called:
File (assuming local storage disk): vendor\league\flysystem\src\Adapter\Local.php
public function copy($path, $newpath)
{
$location = $this->applyPathPrefix($path);
$destination = $this->applyPathPrefix($newpath);
$this->ensureDirectory(dirname($destination));
return copy($location, $destination);
}
The line
$location = $this->applyPathPrefix($path);
will prepend the root path as defined in config/filesystems.php
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
As I can see in your code your files are are not stored in storage/app,
so i think you have to change this to 'root' => storage_path()
So if you want to use Storage::copy() you have just to provide paths relative to that folder. And as it is hard to see, how you could achieve this, take a look at that.
foreach($oIterator as $oFile) {
if ($oFile->getFilename() == 'icon.png') {
$icons[filesize($oFile->getPath().'/icon.png')] = $oIterator->getSubPath().'/icon.png';
}
}
There is RecursiveDirectoryIterator::getSubPath (Although quite undocumentated, which would return you the current subpath of your iterations. In your case relative to $extractFolder.'/res'.
Now you have to make sure you are calling
Storage::copy($icon_source, $generated_icon_file);
Where $icon_source and $generated_icon_file are relative to your defined 'root'.
Insert / before var/www/apk.land/storage/extracted_apks/res/drawable-xxxhdpi-v4/icon.png
The path is relative to current directory. Buy if you add /, the path is absolute from root directory.
you have to choose the right disk somthing like this
$file2 ='public/'.$page->thumbnail;
$destination = 'public/bundles/ttt.png';
if( Storage::disk('local')->exists($file2)){
Storage::disk('local')->copy($file2,$destination);
}

how do I know if a file exists in the current directory tree

I want to check if a file path is in current directory tree.
Suppose the parameter is given as js/script.js. My working directory (WD) is /home/user1/public_html/site
Now for current WD if someone supplies js/script.js I can simply check it by appending to the WD. It works for such normal path. But if anyone (may be an attacker) wants to pass ../../../../etc/password it'd be a problem.
I know it can be suppressed by removing the .. characters using some RegEx. And that will solve it for sure. But I want to know how can I create some sort of chrooted environment sot that whatever path/to/script is passed it will be searched under WD?
Edit:
I am aware of http://php.net/chroot. it requires your app to run with root privileges.
http://php.net/manual/en/function.realpath.php
$chroot = '/var/www/site/userdata/';
$basefolder = '/var/www/site/userdata/mine/';
$param = '../../../../etc/password';
$fullpath = realpath($basefolder . $param);
if (strpos($fullpath, $chroot) !== 0) {
// GOTCHA
}

Drupal 6 Programmatically adding an image to a FileField

How do I programmatically add an image to a file field? I have an url/filepath of an image that I wish to upload. I have tried the following:
$newNode->field_profile_image[0]['value'] = 'http://www.mysite.com/default.gif';
But it does not seem to work.
I have also tried:
$newNode->field_profile_image[0]['value'] = 'sites/default/files/default.gif';
The file does not need to be external to the webiste. I am happy to have it anywhere on the site in question.
You're probably going to have to use hook_nodeapi to set this correctly. You're going to want to modify it under the "Insert" operation. Make sure that you resave the node after you've added the required fields.
Drupal wants to map the image to an entry in the file table, so simply setting the URL will not work. First off, if it's a remote file, you can use the function listed in the Brightcove module on line 176, brightcove_remote_image, to grab the image and move it into your local directory.
Once the remote image is moved into place, you need to save it into the files table and then properly configure the node's property. I've done it in this method:
////// in NodeAPI /////
case "insert":
$node->field_image[0] = _mymod_create_filearray($image_url);
node_save($node);
This writes the files record, and then returns a properly formatted image array.
///// mymod_create_filearray /////
function _mymod_create_filearray($remote_url){
if ($file_temp = brightcove_remote_image($remote_url)) {
$file = new stdClass();
$file->filename = basename($file_temp);
$file->filepath = $file_temp;
$file->filemime = file_get_mimetype($file_temp);
$file->filesize = filesize($file_temp);
$file->uid = $uid;
$file->status = FILE_STATUS_PERMANENT;
$file->timestamp = time();
drupal_write_record('files', $file);
$file = array(
'fid' => $file->fid,
'title' => basename($file->filename),
'filename' => $file->filename,
'filepath' => $file->filepath,
'filesize' => $file->filesize,
'mimetype' => $mime,
'description' => basename($file->filename),
'list' => 1,
);
return $file;
}
else {
return array();
}
}
And that should do it. Let me know if you have any questions.
Check out my Answer to a similar question from a while ago, where I describe how we did pretty much exactly what you need (if I understood the problem correctly).
The main point is to use the field_file_save_file() function from the filefield module for attaching a file (during hook_nodeapi, on operation presave), which will do most of the work for you (more or less what jacobangel's '_mymod_create_filearray()' tries to do, but more tuned to filefields needs, including validation).
This assumes that the file already exists on the servers filesystem somewhere (usually in /tmp), and will correctly 'import' it into Drupal, with corresponding entries in the files table, etc. If you want to import files from remote URLs, you'll need to add the additional step of fetching them as a separate task/functionality first.
As mentioned in the answer linked above, I ended up using the code from the Remote File module as an example for a custom implementation, as we needed some project specific additions - maybe you can use it more directly for your purposes.
using nodeapi you should be able to set the value like you are trying to in the code example, but only or local images. You will most likely need to have the images in the "files" folder in your drupal install, but if that is set up everything else should work without a hitch. When using the nodeapi all the things that would normally happen when you save a node using a form would happen, such as updating the files table etc.
If you wanted to pull the image from the remote site using a module like feeds make it possible to pull the remote images, and create nodes. Depending on your use case you could either use it, or take a look at how it pulls the images and maps them to local files.
What you try will not work. Drupal offers no way to handle remote files, without the use for a module. AFAIK there is no module that offers an API to upload remote files trough.
Here is a quick example taken from one of my projects.
$node = new stdClass;
$node->title = 'Example Callout';
$node->type = 'wn_hp_callout';
// Search examples directory to attach some images.
$callouts_dir = drupal_get_path('module', 'wn_hp_callout').'/imgs/examples/';
$callout_imgs = glob($callouts_dir.'*.{jpg,jpeg,png,gif}',GLOB_BRACE);
// Now add the images and provide imagefield extended additional text.
foreach($callout_imgs as $img) {
$img_info = pathinfo($img);
$field = field_file_save_file($img, array(), file_directory_path() .'/example_callouts/');
if( !isset($field['data']) ) {
$field['data'] = array();
}
$field['data']['title'] = ucwords(str_replace('_',' ',$img_info['filename']));
$field['data']['alt'] = 'This is alt text.';
$node->field_wn_hp_callout_image[] = $field;
}
$node = node_submit($node);
node_save($node);

Categories