PHP - FTP Server - Unable to switch to newly created directory (sometimes!) - php

What I am trying to do
Connect to FTP server
Create new directories
Upload file into one of those new directories
What's going wrong?
Ok, so there is nothing wrong with the ftp connection, I am logging in with the credentials, switching to the relevant starting directory, which in this case is a directory on the server called "test".
At the start, this "test" directory is empty, as I just created it.
What I am then doing is taking a file from my local machine and attempting to upload that the remote path test/Folder1/Sub1/Sub2/Myfile.txt on the server.
To do this, I first split the path by "/" and check to see if I can switch to each of those directories, and if I can't, then I create them:
(You'll have to excuse the debugging dumps in the code)
// Split the path by directories and try and create all of them if they don't exist
$workingDir = $this->dir;
$newPath = str_replace($this->dir, "", $path);
$split = array_filter( explode("/", $newPath) );
if ($split)
{
foreach($split as $dir)
{
// Can we change to it?
if (!#ftp_chdir($this->conn, $workingDir . '/' . $dir)){
// Try and make the directory
if (!#ftp_mkdir($this->conn, $workingDir . '/' . $dir)){
var_dump("failed to make: " .$workingDir . '/' . $dir );
return false;
}
// Set the permissions to the default
$this->chmod = 0777;
ftp_chmod($this->conn, $this->chmod, $workingDir . '/' . $dir);
var_dump("Successfully made directory: " . $workingDir . '/' . $dir . " with permissions: " . $this->chmod);
}
$workingDir .= '/' . $dir;
}
}
This works fine, and I get the output of:
'Successfully made directory: /home/conn/test/Folder1 with permissions: 511' (length=74)
'Successfully made directory: /home/conn/test/Folder1/Sub1 with permissions: 511' (length=79)
'Successfully made directory: /home/conn/test/Folder1/Sub1/Sub2 with permissions: 511' (length=84)
If i exit at this point I can confirm the directories now exist on the remote server, with permissions of 0777
Next bit is to do the file upload into the new directory:
var_dump("Use this new name: {$newName}");
$filename = (($newName) ? $newName : $file->getFileName());
var_dump("working in remote directory: {$this->dir}");
var_dump("upload to remote: {$filename}, from: {$file->getFullPath()}");
// Change to that directory?
$split = explode("/", $filename);
$realFileName = array_pop($split);
$filedir = implode("/", $split);
var_dump("calling ftp_chdir() to : {$filedir}");
if (!#ftp_chdir($this->conn, $filedir)){
var_dump("couldn't change to {$filedir}");
return false;
};
var_dump( ftp_pwd($this->conn) );
var_dump("actual file name: " . $realFileName);
return ftp_put($this->conn, $realFileName, $file->getFullPath(), FTP_BINARY);
And again, this works fine. I get all the expected dumps:
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:121:string 'Use this new name: Folder1/Sub1/Sub2/Myfile.txt' (length=47)
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:124:string 'working in remote directory: /home/conn/test' (length=44)
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:125:string 'upload to remote: Folder1/Sub1/Sub2/Myfile.txt, from: C:\Ampps\www\duckfusioncore\app\lotto\tmp\tmp-nsEMH1RsDX' (length=110)
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:131:string 'calling ftp_chdir() to : Folder1/Sub1/Sub2' (length=42)
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:139:string '/home/conn/test/Folder1/Sub1/Sub2' (length=33)
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:140:string 'actual file name: Myfile.txt' (length=28)
C:\Ampps\www\duckfusioncore\app\lotto\controllers\IndexController.php:28:boolean true
All the directories exist. The file is uploaded. All is well.
Except, if I now change the path I want to upload to and change toa new sub directory, which doesn't yet exist, e.g: Folder1/Sub1/NewSub/Myfile.txt
This is where it all goes wrong.
So the script runs through the create directory part again, and it successfully creates the new directory "NewSub", with the exact same permissions as before, and I can confirm that the directory exists on the remote server.
Except now when we get to the upload part, even though the directory exists, and this worked previously as we saw, it is unable to change to that new directory:
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:121:string 'Use this new name: Folder1/Sub1/NewSub/Myfile.txt' (length=49)
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:124:string 'working in remote directory: /home/conn/test' (length=44)
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:125:string 'upload to remote: Folder1/Sub1/NewSub/Myfile.txt, from: C:\Ampps\www\duckfusioncore\app\lotto\tmp\tmp-XJYdx8eduL' (length=112)
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:131:string 'calling ftp_chdir() to : Folder1/Sub1/NewSub' (length=44)
C:\Ampps\www\duckfusioncore\sys\lib\helpers\datastore\stores\FTPStore.php:135:string 'couldn't change to Folder1/Sub1/NewSub' (length=38)
C:\Ampps\www\duckfusioncore\app\lotto\controllers\IndexController.php:28:boolean false
The directory 100% exists. And it has the same 0777 permissions as the other ones.
I'm just very confused as to why it is unable to switch to the directory, when it defintely exists and it has the same permissions as the other directories, which it had no problem switching to before...
Has anyone got any clue what might be the problem? Is there any extra debugging I can do on the ftp_chdir() function call to see if it gives me a reason?
Thanks.

Related

PHP ftp_nlist returning false when retrieving large directory?

What I'm trying to do:
Use PHP ftp_nlist to retrieve the contents of a directory on the FTP server
The problem:
For directories that contain a lot of files (the one I encountered the problem on has nearly 40 thousand files, and no subfolders), the ftp_nlist function is returning false. For directories that are not as large, the ftp_nlist function returns an array of filenames as expected.
What I've tried:
Enabling passive mode (it already was enabled, but I see it as a common suggestion)
Adding ftp_set_option($conn_id, FTP_USEPASVADDRESS, false); after my ftp_login
using ftp_chdir, although my folder names never have spaces anyways
echoing error_get_last() after ftp_nlist returns false. The error show seems unrelated, but is shown below.
My code:
In case it is useful, here is the function I have created. What it is supposed to do is...
take in $fm (filemaker, unrelated to this problem)
take in $FTPConnectionID (the ftp connection I established in the prior to the function call)
take in $FolderPath (the path of the folder on the FTP server for which I want to list files/subfolders recursively - ex: "SomeFolder/Testing")
take in $TextFile (I am writing the paths of every file found on the FTP server to a text file, which was created prior to calling the function)
function createAuditFile($fm, $FTPConnectionID, $FolderPath, $TextFile) {
echo "createAuditFile called for " . $FolderPath . "\n";
//Get the contents of the given path. Will include files and folders.
$FolderContents = ftp_nlist($FTPConnectionID, $FolderPath);
if($FolderContents == false) {
echo "Couldn't get " . $FolderPath . "\n";
echo "ERROR: " . print_r(error_get_last()) . "\n";
} else {
print_r($FolderContents);
}
//Loop through the array, call this function recursively if a folder is found.
if(is_array($FolderContents)) {
foreach($FolderContents as $Content) {
//Create a varaible for the folder path
$ContentPath = $FolderPath . "/" . $Content;
//Call the function recursively if a folder is found
if(pathinfo($Content, PATHINFO_EXTENSION) == "") {
createAuditFile($fm, $FTPConnectionID, $ContentPath, $TextFile);
echo "Recursive call for " . $ContentPath . "\n";
//If a file is found, add the file ftp path to our array
} else {
echo "Writing to file: " . $ContentPath . "\n";
fwrite($TextFile, $ContentPath . "\n");
}
}
}
}
I can provide other code if needed, but I think my question is less of a coding issue, and more of an understanding ftp_nlist issue. I've been stuck on this for hours, so any help is appreciated. And like I said, this function works just fine for most folder paths passed to it, the problem is when there are tens of thousands of files within the folder. Thank you!

cakephp - create new directory inside webroot

I want to create a new directory(folder) inside the webroot so i can create/edit files inside that new folder.
My code is:
$path = 'files' . DS . 'pathtofolder';// $folder_name;
$folder = new Folder($path);
if (!is_null($folder->path)) { //this is to verify if created
echo "Exists";
}
else{
echo "Doesnt exist";
}
The result is always doesnt exist. And i cant find any created folder inside my cakephp folders.
I tried to change 'files' for 'webroot' but it doesnt work.
Is this the correct code to create a directory?
try this
$path = 'files' . DS . 'pathtofolder';
$folder = new Folder($path, true, 0755);
if ($folder->path) {
echo "Exists";
}
else{
echo "Doesnt exist";
}
There is a direct PHP call to create directories. So, you can use that. I dont know the cakephp version.
if (!mkdir($structure, 0777, true)) {
die('Failed to create folders...');
}
Also, check if your web-server user www-data has permissions to create directories. Otherwise, on command prompt run this command
addgroup www-data
That will add www-data to enable permissions. You might need to add sudo in some cases to above command if you get permission denied error.
$path = WWW_ROOT . 'webroot' . DS . 'FolderNameYouWantToSet' . DS;
if (!file_exists($path)) {
$oldMask = umask(0);
mkdir($path, 0777, true);
chmod($path, 0777);
umask($oldMask);
}
This will create a folder with 777 permission, you can set the folder permission as per your requirement.
WWW_ROOT is the global cake variable which stores the path up to the project.

php rename (move) from one dir to another that is two levels back

my script file path is:
C:\xampp\htdocs\wordpress\wp-content\plugins\test\test.php
I need to run code from this path that will move images from path:
C:\xampp\htdocs\wordpress\wp-content\uploads\2017\04
to:
C:\xampp\htdocs\wordpress\wp-content\uploads\images
My problem is that I have no idea how to force "rename" go two directories back in path.
I was trying something like:
$srcPath = realpath(dirname(__FILE__) . '/../..' . '/uploads/2017/04/obrazek.png');
error I'm getting atm (I've changed my folder permissions, so maybe it is something with path?):
Warning: rename(C:\xampp\htdocs\wordpress\wp-content\uploads\2017\04\obrazek.png,C:\xampp\htdocs\wordpress\wp-content\uploads\images): access denied
. (code: 5) in C:\xampp\htdocs\wordpress\wp-content\plugins\uploadsdir-manager\test.php on line 16
edit rename code:
$srcPath = realpath(dirname(__FILE__) . '/../..' . '/uploads/2017/04/obrazek.png');
$destPath = realpath(dirname(__FILE__) . '/../..' . '/uploads/images');
/*$srcDir = opendir($srcPath);*/
echo $srcPath ;
sleep(1);
rename($srcPath, $destPath);
From looking at it, it looks wrong
$srcPath = realpath(dirname(__FILE__) . '/../..' . '/uploads/2017/04/obrazek.png');
it should be
$srcPath = realpath(dirname(__FILE__) . '../..' . '/uploads/2017/04/obrazek.png');
You have an extra / slash at the beginning.
It seems that you're in a Windows machine, so you're using the wrong type of slashes: /../..' . '/uploads/2017/04/obrazek.png'
You could try to replace them with backslashes:\..\..\uploads\2017\04\obrazek.png'
Or you could try something like this:
$path = substr($srcPath, 0, strrpos($srcPath, '\20'));
// $path now contaits: 'C:\xampp\htdocs\wordpress\wp-content\uploads';
// Then you can do:
$srcPath = $path . '\images';
Regarding the warning error:
Warning: rename(C:\xampp\htdocs\wordpress\wp-content\uploads\2017\04\obrazek.png,C:\xampp\htdocs\wordpress\wp-content\uploads\images): access denied. (code: 5) in C:\xampp\htdocs\wordpress\wp-content\plugins\uploadsdir-manager\test.php on line 16
It seems that you try to rename a file to directory, so probably you forgot to append the file name to the new path.
$srcPath contains a file. $destPath contains a directory, but it should contain the file name.
I've finally after many hours.. find out how to fix it so here you go:
$srcPath = (realpath (dirname(__FILE__) . '\..\..' . '\uploads\2017\04') . '\obrazek2.png');
$destPath = (realpath (dirname(__FILE__) . '\..\..' . '\uploads\images') . '\obrazek2.png');
rename ($srcPath , $destPath );
The key was adding file name . '\obrazek2.png' after dirname (), because if filename is inside dirname () and it is destination path in rename, then it returns empty... :)
Not sure if I'm clear enough, because of my English, but it works and hopes it will help someone.

symlink directory

Can I get an eyeball on my symlink?
I'm trying to download a file from one directory, while the file actually exists in another.
I've got the actual file, and the symlink in seperate subdirectories, but both reside in the public html(both are web accessible).
I've verified the file and file location on my (shared Linux) server by going to the file directly.
The link is being created (I've used readlink, is_link, and linkinfo), and I can see it when I FTP in.
I believe I am probably just having a misunderstanding of the directory structure.
I put the file here: ./testdownload/
I put the symlink here: ./testDelivery/
<?php
$fileName = "testfiledownload.zip";//Name of File
$fileRepository = "./testdownload/";//Where the actual file lives
$downloadDirectory = "./testDelivery/";//Where the symlink lives
unlink($downloadDirectory . $fileName); // Deletes any previously exsisting symlink (required)
symlink($fileRepository . $fileName, $downloadDirectory . $fileName);
$checkLink = ($downloadDirectory . $fileName);
if (is_link($checkLink))
{
echo ("<br>Symlink reads: " .readlink($checkLink) . "<br>");
echo ("<br>LinkeInfo reads: " . linkinfo($checkLink));
}
?>
<p><a href="<?php echo ("/testDelivery/" . $fileName); ?>"</a>SymLink</p>
<p><a href="<?php echo ("/testdownload/" . $fileName); ?>"</a>regular link</p>
Everything looks right to me....but the link won't work.
Help?
Ultimately, I will put the source data outside the public area...this is just for testing.
(I'm trying to find a better solution for download than chunking out fread which fails for poor connections. (200-400MB files))
My problem (appears) to be not providing the absolute path for the symlink.
I've added the absolute path below to the same code above, to give a working copy:
<?php
$absolutepath = ( $_SERVER['DOCUMENT_ROOT']);
$fileName = "testfiledownload.zip";//Name of File
$fileRepository = "/testdownload/";//Where the actual file lives
$downloadDirectory = "/testDelivery/";//Where the symlink lives
unlink($absolutepath .$downloadDirectory . $fileName); // Deletes any previously exsisting symlink (required)
symlink($absolutepath . $fileRepository . $fileName, $absolutepath. $downloadDirectory . $fileName);
$checkLink = ($absolutepath . $downloadDirectory . $fileName);
if (is_link($checkLink))
{
echo ("<br>Symlink reads: " .readlink($checkLink) . "<br>");
echo ("<br>LinkeInfo reads: " . linkinfo($checkLink));
}
?>
<p><a href="<?php echo ("/testDelivery/" . $fileName); ?>"</a>SymLink</p>
<p><a href="<?php echo ("/testdownload/" . $fileName); ?>"</a>regular link</p>
This original post, is a duplicate (though I didn't see it until now)
Create a valid symlink for PHP file
(Most of the answers given for that question were wrong however--but the original poster figured it out, and it worked for me too)

PHP: How do I rename a directory where the parent directory is variable?

i would like to move the files inside uploads/pension/#SOME_VARIABLE_NUMBER#/#SOME_CONSTANT_NUMBER#/
here is my code:
// move pension statements
// located at uploads/pension/%COMPANY_ID%/%USER_ID%/%HASH%
// so just move the %USER_ID% folder to the new company
$oldPensionDir = "uploads/pension/" . $demo_user[Users::companyID] . "/" . $demo_user[Users::userID] . "/";
$newPensionDir = "uploads/pension/" . $newCompanyID . "/" . $demo_user[Users::userID] . "/";
// see if the user had any files, and if so, move them
if(file_exists($oldPensionDir)) {
// if it doesnt exist, make it
if(!file_exists($newPensionDir))
mkdir($newPensionDir);
// move the folder
rename($oldPensionDir, $newPensionDir);
}
however... when i need to make the directory with the "mkdir" function, i get:
mkdir() [<a href='function.mkdir'>function.mkdir</a>]: No such file or directory
ok, maybe the mkdir won't work, but what about the rename? perhaps that will make the directory if it's not there... nope!
rename(uploads/pension/1001/783/,uploads/pension/1000/783/) [<a href='function.rename'>function.rename</a>]: The system cannot find the path specified. (code: 3)
so, there are two errors. i'm pretty sure if the renaming works, i won't even need the mkdir, but who knows... can anyone tell me why these are errors and how to fix them?
thanks!
EDIT: i've modified the code, and now my only issue is an access problem...
rename(uploads/pension/1000_783/,uploads/pension/1001/783/) [<a href='function.rename'>function.rename</a>]: Access is denied. (code: 5)
below is the new code. basically, i rename it three times (because it has to move through folders, but the final move is what causes the 'access denied' error. the odd part is that even when i delete the new dir and it makes a new one, i set it to have perms 0777!!! whats wrong with this?
// move pension and total reward statements
// located at uploads/pension|total_rewards/%COMPANY_ID%/%USER_ID%/%HASH%
// so just move the %USER_ID% folder to the new company
$oldPensionDir = "uploads/pension/" . $demo_user[Users::companyID] . "/" . $demo_user[Users::userID] . "/";
$tempPensionDir1 = "uploads/pension/" . $demo_user[Users::companyID] . "/" . $demo_user[Users::companyID] . "_" . $demo_user[Users::userID] . "/";
$tempPensionDir2 = "uploads/pension/" . $demo_user[Users::companyID] . "_" . $demo_user[Users::userID] . "/";
$newPensionDir = "uploads/pension/" . $newCompanyID . "/" . $demo_user[Users::userID] . "/";
// see if the user had any files, and if so, move them
if(file_exists($oldPensionDir)) {
// if it doesnt exist, make it
if(!file_exists($newPensionDir))
mkdir($newPensionDir, 0777, true);
// move the folder
// first, move it to the pension directory
rename($oldPensionDir, $tempPensionDir1);
rename($tempPensionDir1, $tempPensionDir2);
// second, move it into the new directory
rename($tempPensionDir2, $newPensionDir);
}
remove the mkdir and only rename:
rename($oldPensionDir, $newPensionDir);
where you always strip down to the directory you want to rename and not its children:
uploads/pension/1001
to
uploads/pension/1000
mkdir() has a recursive parameter you can use to create any parent directories needed for the path

Categories