The following PHP file creates a ZIP File and works as it should.
<?php
$zip = new ZipArchive();
$ZIP_name = "./path/Prefix_" .$date . ".zip";
if ($zip->open($ZIP_name, ZipArchive::CREATE)!==TRUE) {
exit("There is a ZIP Error");
}
if ($zip->open($ZIP_name, ZipArchive::CREATE)==TRUE) {
echo "ZIP File can be created" . "<br>";
}
foreach($list as $element) {
$path_and_filename = "../path_to_somewhere/product_"
. $element
. ".csv";
$zip->addFile($path_and_filename, basename($path_and_filename));
}
echo "numfiles: " . $zip->numFiles . "\n"; // number of element files
echo "status:" . $zip->status . "\n"; // Status "0" = okay
$zip->close();
?>
There is only a small blemish:
The above foreach-loop retrieves elements from an array where all elements are sorted in alphabetical order. After the ZIP-File creation, the files within the ZIP are in different order, maybe caused by different file size.
Is there a way to sort the csv files within the ZIP with PHP later on? I'm new to ZIP creation with PHP an I have not found something helpful in the documentation.
You can't do that, better just sort the file list in your program, not in the file system (-;
Related
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 have an array or over 1,000 files that I need to download to my local PC from my server.
I need to keep it to replicate the same file/folder structure for each file.
Here is an example list of files:
/lib/Zend/EventManager/Filter/FilterIterator.php
/lib/Zend/EventManager/config.php
/lib/Zend/Text/Figlet/themes.php
/lib/Zend/Gdata/Analytics/DataEntry.php
/lib/Zend/Gdata/Analytics/AccountQuery.php
/lib/Zend/Gdata/Calendar/files.php
/lib/Zend/Gdata/Query.php
/lib/Zend/Gdata/Gbase/Feed.php
/lib/Zend/Gdata/Photos.php
/lib/Zend/Gdata/Photos/AlbumFeed.php
/lib/Zend/Gdata/Media/Extension/press.php
/lib/Zend/Gdata/Media/file.php
/lib/Zend/Gdata/Extension/RecurrenceException.php
/lib/Zend/Gdata/Extension/Comments.php
/lib/Zend/Gdata/Extension/Recurrence.php
/lib/Zend/Gdata/Extension/Rating.php
To create the folders, navigate to them with FTP, and then download them would take me all day long! How can I do this with PHP?
These files cannot be accessed in the browser with a URL so I have to use the file path.
UPDATE
Here is what I have tried so far using PHP ZipArchive...
files.txt
Test file to test a sample of the files I will need. Final result will be over 1,000 files
lib/Zend/EventManager/Filter/FilterIterator.php
lib/Zend/EventManager/config.php
lib/Zend/Text/Figlet/themes.php
lib/Zend/Gdata/Analytics/DataEntry.php
lib/Zend/Gdata/Analytics/AccountQuery.php
lib/Zend/Gdata/Calendar/files.php
lib/Zend/Gdata/Query.php
lib/Zend/Gdata/Gbase/Feed.php
lib/Zend/Gdata/Photos.php
lib/Zend/Gdata/Photos/AlbumFeed.php
lib/Zend/Gdata/Media/Extension/press.php
lib/Zend/Gdata/Media/file.php
lib/Zend/Gdata/Extension/RecurrenceException.php
lib/Zend/Gdata/Extension/Comments.php
lib/Zend/Gdata/Extension/Recurrence.php
lib/Zend/Gdata/Extension/Rating.php
lib/Zend/Gdata/Books/VolumeQuery.php
lib/Zend/Gdata/Books/VolumeFeed.php
lib/Zend/Gdata/Exif/themes.php
lib/Zend/Gdata/MimeBodyString.php
lib/Zend/Gdata/HttpAdapterStreamingProxy.php
lib/Zend/Gdata/Spreadsheets/Extension/test.php
lib/Zend/Gdata/Spreadsheets/ListEntry.php
lib/Zend/Gdata/Gapps/Query.php
lib/Zend/Gdata/Gapps/GroupQuery.php
lib/Zend/Gdata/Gapps/EmailListRecipientQuery.php
lib/Zend/Gdata/Gapps/Error.php
lib/Zend/Gdata/Gapps/OwnerFeed.php
lib/Zend/Gdata/Gapps/alias.php
lib/Zend/Gdata/Gapps/MemberQuery.php
lib/Zend/Gdata/Gapps/EmailListQuery.php
lib/Zend/Gdata/Gapps/NicknameFeed.php
lib/Zend/Gdata/Exif.php
lib/Zend/Gdata/App/LoggingHttpClientAdapterSocket.php
lib/Zend/Gdata/App/Extension.php
lib/Zend/Gdata/App/MediaEntry.php
lib/Zend/Gdata/App/FeedEntryParent.php
lib/Zend/Gdata/App/AuthException.php
download.php
$zip = new ZipArchive();
$filename = "./test112.zip";
if ($zip->open($filename, ZipArchive::CREATE)!==TRUE) {
exit("cannot open <$filename>\n");
}else{
echo 'zip good';
}
//$zip->addFromString("testfilephp.txt" . time(), "#1 This is a test string added as testfilephp.txt.\n");
//$zip->addFile("lib/Zend/files2.txt" ,"lib/Zend/EventManager/test.php" );
// list of files to download
$lines = file('files.txt');
// Loop through our array of files from the files.txt file
foreach ($lines as $line_num =>$file) {
//echo "Line #<b>{$line_num}</b> : " . htmlspecialchars($file) . "<br />\n";
// Add files to Zip file incliuding folder structure
$zip->addFile($file,$file);
echo $file;
}
// show number of files in new zip file and close zip archive
echo "numfiles: " . $zip->numFiles . "\n";
echo "status:" . $zip->status . "\n";
$zip->close();
Result
This creates my zip file however instead of adding all files, it only adds the last file in my files array to the zip archive! In this example that is lib/Zend/Gdata/App/AuthException.php
As you have SSH access, you could simply run this on the server:
# Change '*.php' to whatever you want to retrieve:
find . -name '*.php' -print | zip archive.zip -#
Then you can get the file archive.zip via scp or ftp.
I would like to check that a file uploaded to my OpenShift app has a text extension (.txt or .tab). Following some advice given here I wrote the following code, with echoes added to help debug:
$AllowedExts = array('txt','tab');
echo "AllowedExts: " . $AllowedExts[0] . " and " . $AllowedExts[1] . "<br>";
$ThisPath = $_FILES['uploadedfile']['tmp_name'];
echo "ThisPath: " . $ThisPath . "<br>";
$ThisExt = pathinfo($ThisPath, PATHINFO_EXTENSION);
echo "ThisExt: " . $ThisExt . "<br>";
if(!in_array($ThisExt,$AllowedExts) ) {
$error = 'Uploaded file must end in .txt or .tab';
}
echo "error echo: " . $error . "<br>";
On uploading any file, the echoed response was:
AllowedExts: txt and tab
ThisPath: /var/lib/openshift/************/php/tmp/phpSmk2Ew
ThisExt:
error echo: Uploaded file must end in .txt or .tab
Does this mean that OpenShift is renaming the file upon upload? How do I get the original filename and then check its suffix? More generally, is there a better way to check the file type?
$_FILES['uploadedfile']['tmp_name'] contains the name of a temporary file on the server (which can be moved with move_uploaded_file()). If you want to check the original name of the uploaded file on the client machine use $_FILES['uploadedfile']['name'].
That's not an Open Shift issue, it's the standard way of PHP.
For further details see http://php.net/manual/en/features.file-upload.post-method.php
For other ways to detect the file type see http://php.net/manual/en/ref.fileinfo.php
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
File creation time
I want to get all the files in a directory along with its date of creation in array.
I saw the scandir function, but it returns even the directory which I don't want. I also figured out that filemtime($filename) function can return the file created date. Just not able to put it together.
in these days of PHP > 5 you should be using the provided classes for this kind of thing. For this particular task you should be using the FilesystemIterator for example:-
$iterator = new FilesystemIterator('D:/dev/');
foreach($iterator as $fileInfo){
if($fileInfo->isFile()){
$cTime = new DateTime();
$cTime->setTimestamp($fileInfo->getCTime());
echo $fileInfo->getFileName() . " file Created " . $cTime->format('Y-m-d h:i:s') . "<br/>\n";
}
if($fileInfo->isDir()){
$cTime = new DateTime();
$cTime->setTimestamp($fileInfo->getMTime());
echo $fileInfo->getFileName() . " dir Modified " . $cTime->format('Y-m-d h:i:s') . "<br/>\n";
}
}
Here $fileInfo is an instance of SplFileInfo which can give you all the information about a file or directory you could ever want.
if ($handle = opendir('/path/to/files')) {
while (false !== ($file = readdir($handle))) {
echo filemtime($file)." ".$file;
}
closedir($handle);
}
Use scandir to get all the files and sub-directory of main directory
then use is_file function of php.
If you want to access specific extensions file please use glob function of php
Here is the example :
How to get only images using scandir in PHP?
I hope this helps.
Hi I am attempting to create a new zip file, I have recompiled PHP enabling ZIP and here is my code
if($_SERVER['REQUEST_METHOD'] == "POST"){
$zip = new ZipArchive;
if($zip->open("/home/user1joe/public_html/upload/test.zip",ZIPARCHIVE::CREATE)!= true) {
echo "error file did not up upload";
exit(0);
}
foreach ($_FILES["images"]["error"] as $key => $error)
if ($error == UPLOAD_ERR_OK) {
$temp_name = $_FILES["images"]["tmp_name"][$key];
$new_name = $_FILES['images']['name'][$key];
var_dump($temp_name);
var_dump($new_name);
if(file_exists($temp_name)){
$zip->addFile($temp_name,$new_name);
move_uploaded_file($temp_name, "upload/".$new_name);
} else
echo "error file does not exist";
echo "numfiles: " . $zip->numFiles . "\n";
echo "status:" . $zip->status . "\n";
}
$res = $zip->close();
var_dump($res);
}
$zip->close() is returning false and no zip is being created.
I get no errors building up to this
$zip->numFiles shows there are files in the archive
The folder where I want to create the zip has writable permission
I am a bit lost of what else I can test for, any ideas would be great!
zip->close() method do some processing (actual compression, or just getting/writing file attributes), which require that files, added to archive, to exist.
However you are removing input files from the original location with move_uploaded_file() call.