I am not sure what the issue is, I am trying to delete a file in PHP. The file has spaces in the filename.
So here, is what I am doing :
$filename = "'" . "files/".$results["filename"] . "'";
if (file_exists($filename))
{
$success = unlink($filename);
}
else
{
$echo "Could not Delete the file " . $filename;
}
However, I am getting a File Not exist error. However, I can see that file exists in the folder.
I know it might have been asked a million times, however I could not find it.
Related
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!
I have a function that writes ~120Kb-150Kb HTML and meta data on ~8000 .md files with fixed names every few minutes:
a-agilent-technologies-healthcare-nyse-us-39d4
aa-alcoa-basic-materials-nyse-us-159a
aaau-perth-mint-physical-gold--nyse-us-8ed9
aaba-altaba-financial-services-nasdaq-us-26f5
aac-healthcare-nyse-us-e92a
aadr-advisorshares-dorsey-wright-adr--nyse-us-d842
aal-airlines-industrials-nasdaq-us-29eb
If file does not exist, it generates/writes quite fast.
If however the file exists, it does the same much slower, since the existing file carries ~150KB data.
How do I solve this problem?
Do I generate a new file with a new name in the same directory, and unlink the older file in the for loop?
or do I generate a new folder and write all files then I unlink the previous directory? The problem with this method is that sometimes 90% of files are being rewritten and some remain the same.
Code
This function is being called in a for loop, which you can see it in this link
public static function writeFinalStringOnDatabase($equity_symbol, $md_file_content, $no_extension_filename)
{
/**
*#var is the MD file content with meta and entire HTML
*/
$md_file_content = $md_file_content . ConfigConstants::NEW_LINE . ConfigConstants::NEW_LINE;
$dir = __DIR__ . ConfigConstants::DIR_FRONT_SYMBOLS_MD_FILES; // symbols front directory
$new_filename = EQ::generateFileNameFromLeadingURL($no_extension_filename, $dir);
if (file_exists($new_filename)) {
if (is_writable($new_filename)) {
file_put_contents($new_filename, $md_file_content);
if (EQ::isLocalServer()) {
echo $equity_symbol . " 💚 " . ConfigConstants::NEW_LINE;
}
} else {
if (EQ::isLocalServer()) {
echo $equity_symbol . " symbol MD file is not writable in " . __METHOD__ . " 💔 Maybe, check permissions!" . ConfigConstants::NEW_LINE;
}
}
} else {
$fh = fopen($new_filename, 'wb');
fwrite($fh, $md_file_content);
fclose($fh);
if (EQ::isLocalServer()) {
echo $equity_symbol . " front md file does not exit in " . __METHOD__ . " It's writing on the database now 💛" . ConfigConstants::NEW_LINE;
}
}
}
I haven't programmed in PHP for years, but this question has drawn my interest today. :D
Suggestion
How do I solve this problem?
Do I generate a new file with a new name in the same directory, and unlink the older file in the for loop?
Simply use the 3 amigos fopen(), fwrite() & fclose() again, since fwrite will also overwrite the entire content of an existing file.
if (file_exists($new_filename)) {
if (is_writable($new_filename)) {
$fh = fopen($new_filename,'wb');
fwrite($fh, $md_file_content);
fclose($fh);
if (EQ::isLocalServer()) {
echo $equity_symbol . " 💚 " . ConfigConstants::NEW_LINE;
}
} else {
if (EQ::isLocalServer()) {
echo $equity_symbol . " symbol MD file is not writable in " . __METHOD__ . " 💔 Maybe, check permissions!" . ConfigConstants::NEW_LINE;
}
}
} else {
$fh = fopen($new_filename, 'wb');
fwrite($fh, $md_file_content);
fclose($fh);
if (EQ::isLocalServer()) {
echo $equity_symbol . " front md file does not exit in " . __METHOD__ . " It's writing on the database now 💛" . ConfigConstants::NEW_LINE;
}
}
For the sake of DRY principle:
// It's smart to put the logging and similar tasks in a separate function,
// after you end up writing the same thing over and over again.
public static function log($content)
{
if (EQ::isLocalServer()) {
echo $content;
}
}
public static function writeFinalStringOnDatabase($equity_symbol, $md_file_content, $no_extension_filename)
{
$md_file_content = $md_file_content . ConfigConstants::NEW_LINE . ConfigConstants::NEW_LINE;
$dir = __DIR__ . ConfigConstants::DIR_FRONT_SYMBOLS_MD_FILES; // symbols front directory
$new_filename = EQ::generateFileNameFromLeadingURL($no_extension_filename, $dir);
$file_already_exists = file_exists($new_filename);
if ($file_already_exists && !is_writable($new_filename)) {
EQ::log($equity_symbol . " symbol MD file is not writable in " . __METHOD__ . " 💔 Maybe, check permissions!" . ConfigConstants::NEW_LINE);
} else {
$fh = fopen($new_filename,'wb'); // you should also check whether fopen succeeded
fwrite($fh, $md_file_content); // you should also check whether fwrite succeeded
if ($file_already_exists) {
EQ::log($equity_symbol . " 💚 " . ConfigConstants::NEW_LINE);
} else {
EQ::log($equity_symbol . " front md file does not exit in " . __METHOD__ . " It's writing on the database now 💛" . ConfigConstants::NEW_LINE);
}
fclose($fh);
}
}
Possible cause
tl;dr To much overhead due to the Zend string API being used.
The official PHP manual says:
file_put_contents() is identical to calling fopen(), fwrite() and fclose() successively to write data to a file.
However, if you look at the source code of PHP on GitHub, you can see that the part "writing data" is done slightly different in file_put_contents() and fwrite().
In the fwrite function the raw input data (= $md_file_content) is directly accessed in order to write the buffer data to the stream context:
Line 1171:
ret = php_stream_write(stream, input, num_bytes);
In the file_put_contents function on the other hand the Zend string API is used (which I never heard before).
Here the input data and length is encapsulated for some reason.
Line 662
numbytes = php_stream_write(stream, Z_STRVAL_P(data), Z_STRLEN_P(data));
(The Z_STR.... macros are defined here, if you are interested).
So, my suspicion is that possibly the Zend string API is causing the overhead while using file_put_contents.
side note
At first I thought that every file_put_contents() call creates a new stream context, since the lines related to creating context were also slightly different:
PHP_NAMED_FUNCTION(php_if_fopen) (Reference):
context = php_stream_context_from_zval(zcontext, 0);
PHP_FUNCTION(file_put_contents) (Reference):
context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
However, on closer inspection, the php_stream_context_from_zval call is made effectively with the same params, that is the first param zcontext is null, and since you don't pass any flags to file_put_contents, flags & PHP_FILE_NO_DEFAULT_CONTEXT becomes also 0 and is passed as second param.
So, I guess the default stream context is re-used here on every call. Since it's apparently a stream of type persistent it is not disposed after the php_stream_close() call.
So the Fazit, as the Germans say, is there is apparently either no additional overhead or equally same overhead regarding the creation or reusing a stream context in both cases.
Thank you for reading.
I can't get the picture to display/show when viewing, although the files are already stored in the database (table 'menu') http://i.imgur.com/wo1w90H.png. Also when I upload the images all at once, their file name would change automatically. I don't know how and why this happens. I use array to upload multiple images.
if (isset($_POST["Submit"])) {
--some code here--
if (isset($_POST["id_list"])) {
// if id list available
foreach($_POST["id_list"] AS $id) {
--some code here--
/* Handle file upload */
if ($_FILES['upload']['error'][$id] == 'UPLOAD_ERR_OK') {
$path = "images/newmenu/";
$path_parts = pathinfo($_FILES["upload"]["name"][$id]);
$extension = $path_parts['extension'];
$picture = md5(uniqid()) . "." . $extension;
if (move_uploaded_file($_FILES['upload']['tmp_name'][$id], $path . "/" . $picture)) {
$update = " UPDATE menu
SET MenuPicture='$picture'
WHERE MenuID=$id";
$mysqli->query($update) or die(mysqli_error($mysqli));
}
}
}
}
}
}
Below is the form and yes it does include enctype="multipart/form-data"
<input type="file" multiple name="upload[' . $id . ']" value="' . $record["MenuPicture"] . '">
Filename changes because you are generating it this way
$picture = md5(uniqid()) . "." . $extension;
uniqid() is based on current time and hashing it will cause the filename to change everytime
When I upload the images all at once, their file name would change automatically
It was due to this:
$picture = md5(uniqid()) . "." . $extension;
// And later
move_uploaded_file($_FILES['upload']['tmp_name'][$id], $path . "/" . $picture)
Basically, you are moving your uploaded file to a new filename for your image file, which is generated using uniqid() and hashed with md5(), with the file extension appended at the end.
I can't get the picture to display/show when viewing
How are you trying to display the picture? Is it from web browser, or you go straight to the directory and open from there? What error(s) did you get, if any?
Actually, have you tried to go to the directory and see whether the file is created inside the images/newmenu/ directory?
Also, for the target upload directory, you might want to append it with $_SERVER['DOCUMENT_ROOT'] so that the target directory is not dependent on where your script is located, but it's always based on the root.
By the way, you might know already, but there is an entry in PHP manual page on uploading multiple files
I have been using an upload script on my server, like below
$newname = time() . '_' . $_FILES[$file]["name"];
if (strtolower(end(explode('.', $_FILES[$file]["name"]))) != 'pdf' AND $file != "damage_attachment_damageform_1" AND $file != "damage_attachment_damageform_2" AND $file != "damage_attachment_damageform_3" AND $file != "damage_attachment_damageform_4") {
if (move_uploaded_file($_FILES[$file]["tmp_name"], $_SERVER['DOCUMENT_ROOT'] . '/components/com_fleet/uploads/docs/' . $newname)) {
$images[] = $_SERVER['DOCUMENT_ROOT'] . '/components/com_fleet/uploads/docs/' . $newname;
$docs[] = $_SERVER['DOCUMENT_ROOT'] . '/components/com_fleet/uploads/docs/' . $newname;
} else {
die();
}
}
It uploads an image fine, but since a few days a get a Warning: move_uploaded_file(): Unable to move error. Ive seen these a dozen of times while learning to program, so I did all the usual stuff, check paths, the $_FILES[$file]["error"] and check all the right CHMODs. All is fine, path is spot-on, chmod is too, no errors etc...
1 extra weird thing I noticed the file does get written to the right /docs map but its Filesize is empty, and move_upload_file still sends false...
What am I forgetting? CHOWN maybe? And how can I solve that, I dont have SSH access or something.
Graa after an hour I now found out what was wrong, server Disk Quota was exceeded. Maybe people can still benefit from my problems...
I have strong reason to believe that both functions rename() and unlink() are asynchronous, which, from my understanding, means that when the functions are called, the code below them are continued before it finishes its procedures on the filesystem. This is a problem for the internet app I'll explain below, because later code depends on these changes to already be set in stone. So, is there a way to make both synchronous, so that the code reader freezes when it hits these functions, until all of its tasks are fully carried out on the filesystem?
Here is the code in delete-image.php, which is called by ajax from another admin-images.php(the latter will not be shown):
`
foreach ($dirScan as $key => $value) {
$fileParts = explode('.', $dirScan[$key]);
if (isset($fileParts[1])) {
if ((!($fileParts[1] == "gif") && !($fileParts[1] == "jpg")) && (!($fileParts[1] == "png") && !($fileParts[1] == "jpg"))) {
unset($dirScan[$key]);
}
} else {
unset($dirScan[$key]);
}
}
$dirScan = array_values($dirScan);
// for thumbnail
$file = 'galleries/' . $currentGal . '/' . $currentDir . "/" . $dirScan[$imageNum - 1];
unlink($file);
for ($i = ($imageNum - 1) + 1; $i < count($dirScan); $i++) {
$thisFile = 'galleries/' . $currentGal . '/' . $currentDir . '/' . $dirScan[$i];
$thisSplitFileName = explode('.', $dirScan[$i]);
$newName = 'galleries/' . $currentGal . '/' . $currentDir . "/" . ($thisSplitFileName[0] - 1) . "." . $thisSplitFileName[1];
rename($thisFile, $newName);
}
// for large image
$fileParts = explode('.', $dirScan[$imageNum - 1]);
$file = 'galleries/' . $currentGal . '/' . $currentDir . "/large/" . $fileParts[0] . "Large." . $fileParts[1];
unlink($file);
for ($i = ($imageNum - 1) + 1; $i < count($dirScan); $i++) {
$thisSplitFileName = explode('.', $dirScan[$i]);
$thisFile = 'galleries/' . $currentGal . '/' . $currentDir . '/large/' . $thisSplitFileName[0] . "Large." . $thisSplitFileName[1];
$newName = 'galleries/' . $currentGal . '/' . $currentDir . "/large/" . ($thisSplitFileName[0] - 1) . "Large." . $thisSplitFileName[1];
rename($thisFile, $newName);
}
sleep(1);
echo 'deleted ' . $dirScan[$imageNum - 1] . " successfully!";
} else {
echo "please set the post data";
} ?>`
After this script returns its completed text, admin-images.php triggers a new function which populates an image table from these renamed and trimmed files. Sometimes it displays old names and files that were suppose to be deleted, and a simple page refresh gets rid of them. This seems to suggest that the above php script is running through all the code and spitting out echoed text to the mainfile before it completes its file-system manipulation (All of this other code is long and complicated, and hopefully unnecessary for the discussion at hand).
You'll notice, I've tried a sleep() function to halt the php script to hopefully give it time to finish. This is an ineligent, and problematic way of doing things, because I have to put a large amount of time to insure it works every-time, but I don't want the user to wait longer than she / he has to.
Mind that file-systems often use caches to reduce the load. Normally you won't notice, but sometimes you need to clear the cache if you need to have the real information. Check the configuration of your file-system if your issue is file-system related.
PHP itself uses a cache as well for some file-operations, so clear that, too.
See clearstatcache to clear the PHP stat cache.
Take note that this is a "view" issue, the file is actually deleted on disk, but PHP might still return it's there (until you clear the cache).
I suppose they are not asynchronous, because they return a result telling if the operation was successful or not.
I believe the problem happens because when you run scandir after making the modifications, it may be using "cached" data, from memory, instead of re-scanning the file system.
rename() is not, but unlink() is asynchronous on Windows.
Because there seems to be no way of waiting for a pending delete to finish, this answer suggests to rename a file before deleting it. PHP does not seem to do that, so you can assume it's asynchronous.
To use any file operation you are required to use the $_SERVER["DOCUMENT_ROOT"] to make that work. In case you wont do it.. the real operation wont work properly. Also in case you are using the Linux Server then you will be required to set the permissions for the folders in which you want to perform the file operation.
And mind it both the operations are synchronous they are not asynchronous. It also depends on the type of the server or the OS that you are using.