Cannot create large zip file on GAE / Cloud Storage / PHP - php

I've read similar posts on ZipArchive and GAE but they do not solve my problem. Creating the .zip to the GAE tmp folder exhausts memory as the zip may be 1G or more. Therefore, I'm trying to write directly to a storage bucket.
The output below shows the results of running my zip function. I'm sort of new to GAE/Storage so hoping some more eyes could help me out!
One thing I notice is that $zip->filename = /workspace/gs:/X... Why is "workspace" there and why only one "/" after gs:?
If there is some other method to achieve this, I'm open to suggestions!
I have this working in LAMP, but need to move to GAE. Here's my code of the function that does the zipping:
<?php
function create_zip($files = array(),$fileDestinations = array(),$destination='',$overwrite = false) {
$valid_files = array();
if(is_array($files)) {
//cycle through each file
foreach($files as $file) {
//make sure the file exists OR THE ZIP WON'T BE CREATED
if(file_exists($_SESSION['devStorage'].$file)) {
$valid_files[] = $_SESSION['devStorage'].$file;
}else{
$valid_files[] = $_SERVER['DOCUMENT_ROOT']."/images/default.jpg";
}
}
}
//if we have good files...
if(count($valid_files)) {
if (is_writable(str_replace("https://storage.googleapis.com/","gs://",$_SESSION['devStorage']))) {
echo '<br>'.str_replace("https://storage.googleapis.com/","gs://",$_SESSION['devStorage']).' Folder is writable<br>';
} else {
echo '<br>'.str_replace("https://storage.googleapis.com/","gs://",$_SESSION['devStorage']).' Folder not writable<br>';
}
//create the archive
$zip = new ZipArchive();
if($zip->open($destination, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) {
return "<br>can't open ".$destination;
}
//add the files
foreach($valid_files as $file) {
$zip->addFile($file,current($fileDestinations));
next($fileDestinations);
}
//debug
echo '<br>The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
echo '<br>zip->filename = '.$zip->filename;
//close the zip -- done!
if($zip->close()!==true) {
echo '<br>zip->close error';
}
//check to make sure the file exists
if (!file_exists($destination)) {
return "<br>File does not exist at ".$destination;
}else{
return true;
}
} else {
return "no valid files";
}
}
?>
The resulting output:
gs://X.appspot.com/uploads/ Folder is writable
The zip archive contains 139 files with a status of 0
zip->filename = /workspace/gs:/X.appspot.com/uploads/XSpring2021asof122920_06-03-2021_11-18-45
zip->close error
File does not exist at gs://X.appspot.com/uploads/XSpring2021asof122920_06-03-2021_11-18-45
Thanks for any help offered!

Ended up using ZipStream. My function for zipping is below:
/* creates a compressed zip file */
function create_zip($files = array(),$fileDestinations = array(),$destination='') {
// Autoload the dependencies
require 'vendor/autoload.php';
// enable output of HTTP headers
$options = new ZipStream\Option\Archive();
$options->setSendHttpHeaders(true);
$options->setFlushOutput(true);
$options->setZeroHeader(true);
$options->setStatFiles(true);
// create a new zipstream object
$zip = new ZipStream\ZipStream($destination, $options);
$valid_files = array();
if(is_array($files)) {
//cycle through each file
foreach($files as $file) {
if (($_SERVER["REMOTE_ADDR"]=="127.0.0.1") || ($_SESSION["LAMP"]==1)) {
// //make sure the file exists OR THE ZIP WON'T BE CREATED
//if(file_exists($_SERVER["REQUEST_SCHEME"]."://".$_SERVER['HTTP_HOST'].$file)) {
$valid_files[] = $_SERVER["REQUEST_SCHEME"]."://".$_SERVER['HTTP_HOST'].$file;
//}else{
// $valid_files[] = $_SERVER["REQUEST_SCHEME"]."://".$_SERVER['HTTP_HOST']."/images/default.jpg";
//}
}else{
// //make sure the file exists OR THE ZIP WON'T BE CREATED
//if(file_exists($file)) {
$valid_files[] = $file;
//}else{
// $valid_files[] = $_SERVER["REQUEST_SCHEME"]."://".$_SERVER['HTTP_HOST']."/images/default.jpg";
//}
}
}
}
//if we have good files...
if(count($valid_files)) {
foreach($valid_files as $url) {
// Create Temp File
$fp = tmpfile();
// Download File
$ch = curl_init();
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_URL, $url);
curl_exec($ch);
// Force to write all data
fflush($fp);
// Rewind to the beginning, otherwise the stream is at the end from the start
rewind($fp);
// Add File
$zip->addFileFromStream(current($fileDestinations), $fp);
next($fileDestinations);
// Close the Temp File
curl_close($ch);
fclose($fp);
}
// Finish ZIP
$zip->finish();
return true;
}else{
return "no valid files";
}
}

Related

PHP refusing to extract docx as zip from Windows/Temp

$dir = "temp/docx";
$errors = array();
$zip = new ZipArchive;
if($zip->open($file_path) === false){
$errors[] = 'Failed to open file';
}
if (empty($errors)) {
$zip->extractTo($dir,"word/document.xml");
$zip->close();
$files = scandir($dir);
print_r($files);
Ok so, basically for some reason the extraction wont work. After seeing the folders empty, I decided to do a scandir to see if they were deleting after the php finished. Nothing. $files variable outputs nothing (ofcourse apart from .. and .).
The zip is actually a docx file, and after explicitly checking for errors, php seems to think the zip_open works, but I'm not sure if this is just a false positive.
I'm wondering if this is due to the fact this is actually a docx file and I need to explicitly save it as a zip file on the server. Or perhaps because this happens straight after being uploaded and the temp file gets deleted before being able to do anything with it (which I imagine is unlikely, as other formats work fine). Perhaps neither of my assumptions are close, or there's the chance I wrote the whole thing wrong. Any help?
Here you go:
<?php
/*Name of the document file*/
$document = 'demo.docx';
/*Directory*/
$dir = "temp/docx/";
/**Function to extract text*/
function extracttext($filename, $action) {
//Check for extension
$ext = end(explode('.', $filename));
//Check if DOCX file
if($ext == 'docx'){
$dataFile = "word/document.xml";
//else it's probebly an ODT file
} else {
$dataFile = "content.xml";
}
//Create a new ZIP archive object
$zip = new ZipArchive;
// Open the archive file
if (true === $zip->open($filename)) {
// If successful, search for the data file in the archive
if (($index = $zip->locateName($dataFile)) !== false) {
// Index found! Now read it to a string
$text = $zip->getFromIndex($index);
// Load XML from a string
// Ignore errors and warnings
$xml = DOMDocument::loadXML($text, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING);
if($action == "save"){
// Save xml to file
file_put_contents($dir ."word/document.xml", $xml->saveXML());
return "File succesfully saved.";
} else if($action == "text"){
// Remove XML formatting tags and return the text
return strip_tags($xml->saveXML());
}
}
//Close the archive file
$zip->close();
}
// In case of failure return a message
return "File not found";
}
//Save xml file
echo extracttext($document, "save");
//Echo text from file
echo extracttext($document, "text");
?>

Improve my Unzip & Move function - PHP

I'm a PHP novice and so looking for some advice on a PHP function i have created to use within a Wordpress installation.
As you can see from the code below, it runs when one of the admin's press 'Publish' on a pending post.
It takes a Zip file that has been uploaded by a user via Gravity Forms, then unzips ONLY .mp3 extensions. Re-zips and moves all the files to a new folder in our Amazon S3 directory.
The code is pieced together from my limited knowledge and some help along the way with questions on here.
So, here's what i ended up with:
add_action('pending_to_publish', 'unzip_to_s3');
function unzip_to_s3() {
global $post;
global $wpdb;
// Only run function if post is portfolio post type
if ('portfolio' == $post->post_type) {
// Set temp path
$temp_path = '../wp-content/uploads/gravity_forms/1-9e5dc27086c8b2fd2e48678e1f54f98c/2013/02/tmp/';
// Get filename from Zip file
$file = get_post_meta($post->ID, 'file_url', true);
$zip_file = basename($file);
// Create full Zip file path
$zip_file_path = $temp_path.$zip_file;
// Generate unique name for temp sub_folder for unzipped files
$temp_unzip_folder = uniqid('temp_TMS_', true);
// Create full temp sub_folder path
$temp_unzip_path = $temp_path.$temp_unzip_folder;
// Make the new temp sub_folder for unzipped files
if (!mkdir($temp_unzip_path, 0755, true)) {
die('Error: Could not create path: '.$temp_unzip_path);
}
// Unzip files to temp unzip folder, ignoring anything that is not a .mp3 extension
$zip = new ZipArchive();
$filename = $zip_file_path;
if ($zip->open($filename)!==TRUE) {
exit("cannot open <$filename>\n");
}
for ($i=0; $i<$zip->numFiles;$i++) {
$info = $zip->statIndex($i);
$file = pathinfo($info['name']);
if(strtolower($file['extension']) == "mp3") {
file_put_contents($temp_unzip_path.'/'.basename($info['name']), $zip->getFromIndex($i));
} else {
$zip->deleteIndex($i);
}
}
$zip->close();
// Re-zip the unzipped mp3's and store new zip file in temp folder created earlier
$temp_unzip_path = $temp_unzip_path.'/';
$zip = new ZipArchive();
$dirArray = array();
$new_zip_file = $temp_unzip_path.$zip_file;
$new = $zip->open($new_zip_file, ZIPARCHIVE::CREATE);
if ($new === true) {
$handle = opendir($temp_unzip_path);
while (false !== ($entry = readdir($handle))) {
if(!in_array($entry,array('.','..')))
{
$dirArray[] = $entry;
$zip->addFile($temp_unzip_path.$entry,$entry);
}
}
closedir($handle);
} else {
echo 'Failed to create Zip';
}
$zip->close();
// Set Media bucket dir
$bucket_path = '../wp-content/uploads/gravity_forms/1-9e5dc27086c8b2fd2e48678e1f54f98c/2013/02/mixtape2/';
// Generate unique name for sub_bucket
$sub_bucket = uniqid('TMS_', true);
// Create full sub_bucket path
$sub_bucket_path = $bucket_path.$sub_bucket;
// Make the new sub_bucket
if (!mkdir($sub_bucket_path, 0755, true)) {
die('Error: Could not create path: '.$sub_bucket_path);
}
// Move mp3's to new sub_bucket
// Get array of all source files
$files = scandir($temp_unzip_path);
// Identify directories
$source = $temp_unzip_path;
$destination = $sub_bucket_path.'/';
// Cycle through all source files
foreach ($files as $file) {
if (in_array($file, array(".",".."))) continue;
// if move files is successful delete the original temp folder
if (rename($source.$file, $destination.$file)) {
rmdir($temp_unzip_path);
}
}
// Delete original Zip file
unlink($temp_path.$zip_file);
// Update Custom field for new Zip file location
update_post_meta($post->ID, 'file_url', 'http://themixtapesite.com/wp-content/uploads/gravity_forms/1-9e5dc27086c8b2fd2e48678e1f54f98c/2013/02/mixtape2/'.$sub_bucket.'/'.$zip_file);
}
}
Whilst this function does work, we're dealing with large files and so it does take a while to process...
What is happening is when the admin presses publish it triggers this function but the page just sits there until it's finished this function and then will continue. This function can take upto around 5 minutes to run.
I'm looking to optimise this function (in terms of code) but also see if there's a way i can run this in the background so that the admin can carry on with other things and not have to sit there waiting around.
Any help appreciated.
You may want to try to WP cron and schedule the task at that point so that it runs in the background. Here is some resources for that. the basic concept would go something like this.
if ( ! wp_next_scheduled( 'pending_to_publish' ) ) {
wp_schedule_single_event($timestamp,'pending_to_publish');
}
add_action('pending_to_publish', 'unzip_to_s3');
http://wpengineer.com/1908/use-wordpress-cron/
http://codex.wordpress.org/Category%3aWP-Cron_Functions
https://wordpress.stackexchange.com/questions/42694/run-a-cron-job-or-similar-in-the-background-of-wp-after-post-update-create

How to zip the selected files of a HTML form

I have PHP page which lists some songs based on a criteria such as album year or something. I just placed a checkbox near to every song. When a visitor selects some check box and press submit button the form sends each value to a php file. In the PHP file the values are stored in an array.
$files_to_zip=array();
for($i=0;$i<count($_POST['song'];i++)
$files_to_zip[]=$_POST['song'][$i];
//'song' is the name of the checkbox in the HTML form
The problem is the PHP file does not create the zip file with the code i wrote.
function create_zip($files = array(),$destination = '',$overwrite = true) {
//if the zip file already exists and overwrite is false, return false
if(file_exists($destination) && !$overwrite) { return false; }
//vars
$valid_files = array();
//if files were passed in...
if(is_array($files)) {
//cycle through each file
foreach($files as $file) {
//make sure the file exists
if(file_exists($file)) {
$valid_files[] = $file;
}
}
}
//if we have good files...
if(count($valid_files)) {
//create the archive
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
return false;
}
//add the files
foreach($valid_files as $file) {
$zip->addFile($file,$file);
}
//debug
//echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
//close the zip -- done!
$zip->close();
//check to make sure the file exists
return file_exists($destination);
}
else
{
return false;
}
}
$files_to_zip = array();
foreach($_POST['song'] as $fileabc)
{
$files_to_zip[]=$fileabc;
}
//if true, good; if false, zip creation failed
$result = create_zip($files_to_zip,'my-archive.zip');
if($result==true)echo "Success"; else echo "failed";
This is the entire code I use in the PHP.
If I set the value for $files_to_zip dynamically such as,
$files_to_zip=array('link1','link2','link3'..)
it works properly.
What is the bug in my code?

want to download images from server

I want to transfer site from shared hosting server to dedicated server.current server has folder,which includes approx. 16000 images...we can not use FTP to download these much images.and i do not have SSH rights? how can i download images from this shared hosting server.
we can not use FTP to download these much images
Nonsense. FTP (the protocol) is perfectly capable of downloading 16000 files. If your FTP program is causing you trouble, simply pick a better FTP program. If you can handle commandline applications, wget is nice, since it supports recursion and continuation.
Unless they are located within a directory that is within the web root of a web application server you are out of luck.
Zip them all, adapted from http://davidwalsh.name/create-zip-php
<?php
function create_zip($files = array(),$destination = '',$overwrite = false) {
//if the zip file already exists and overwrite is false, return false
if(file_exists($destination) && !$overwrite) { return false; }
//vars
$valid_files = array();
//if files were passed in...
if(is_array($files)) {
//cycle through each file
foreach($files as $file) {
//make sure the file exists
if(file_exists($file)) {
$valid_files[] = $file;
}
}
}
//if we have good files...
if(count($valid_files)) {
//create the archive
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
return false;
}
//add the files
foreach($valid_files as $file) {
$zip->addFile($file,$file);
}
//debug
//echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
//close the zip -- done!
$zip->close();
//check to make sure the file exists
return file_exists($destination);
}
else
{
return false;
}
}
//glob images folder into an array
foreach (glob("*.jpg") as $filename) {
$files_to_zip[]='images/'.$filename;
}
//create the zip
$result = create_zip($files_to_zip,'my-images.zip');
if($result==true){echo'Download Images';
}else{
echo'Could not create zip';}
?>

Passing uploaded files to another part of the script for onward processing

I have searched the forum but the closest question which is about the control stream did not help or I did not understand so I want to ask a different question.
I have an html form which uploads multiples files to a directory. The upload manager that handles the upload resides in the same script with a different code which I need to pass the file names to for processing.
The problem is that the files get uploaded but they don't get processed by the the other code. I am not sure about the right way to pass the $_FILES['uploadedFile']['tmp_name']) in the adjoining code so the files can be processed with the remaining code. Please find below the script.
More specif explanation:
this script does specifically 2 things. the first part handles file uploads and the second part starting from the italised comment extracts data from the numerous uploaded files. This part has a variable $_infile which is array which is suppose to get the uploaded files. I need to pass the files into this array. so far I struggled and did this: $inFiles = ($_FILES['uploadedFile']['tmp_name']); which is not working. You can see it also in the full code sample below. there is no error but the files are not passed and they are not processed after uploading.
<?php
// This part uploads text files
if (isset($_POST['uploadfiles'])) {
if (isset($_POST['uploadfiles'])) {
$number_of_uploaded_files = 0;
$number_of_moved_files = 0;
$uploaded_files = array();
$upload_directory = dirname(__file__) . '/Uploads/';
for ($i = 0; $i < count($_FILES['uploadedFile']['name']); $i++) {
//$number_of_file_fields++;
if ($_FILES['uploadedFile']['name'][$i] != '') { //check if file field empty or not
$number_of_uploaded_files++;
$uploaded_files[] = $_FILES['uploadedFile']['name'][$i];
//if (is_uploaded_file($_FILES['uploadedFile']['name'])){
if (move_uploaded_file($_FILES['uploadedFile']['tmp_name'][$i], $upload_directory . $_FILES['uploadedFile']['name'][$i])) {
$number_of_moved_files++;
}
}
}
}
echo "Files successfully uploaded . <br/>" ;
echo "Number of files submitted $number_of_uploaded_files . <br/>";
echo "Number of successfully moved files $number_of_moved_files . <br/>";
echo "File Names are <br/>" . implode(',', $uploaded_files);
*/* This is the start of a script to accept the uploaded into another array of it own for* processing.*/
$searchCriteria = array('$GPRMC');
//creating a reference for multiple text files in an array
**$inFiles = ($_FILES['uploadedFile']['tmp_name']);**
$outFile = fopen("outputRMC.txt", "w");
$outFile2 = fopen("outputGGA.txt", "w");
//processing individual files in the array called $inFiles via foreach loop
if (is_array($inFiles)) {
foreach($inFiles as $inFileName) {
$numLines = 1;
//opening the input file
$inFiles = fopen($inFileName,"r");
//This line below initially was used to obtain the the output of each textfile processed.
//dirname($inFileName).basename($inFileName,'.txt').'_out.txt',"w");
//reading the inFile line by line and outputting the line if searchCriteria is met
while(!feof($inFiles)) {
$line = fgets($inFiles);
$lineTokens = explode(',',$line);
if(in_array($lineTokens[0],$searchCriteria)) {
if (fwrite($outFile,$line)===FALSE){
echo "Problem w*riting to file\n";
}
$numLines++;
}
// Defining search criteria for $GPGGA
$lineTokens = explode(',',$line);
$searchCriteria2 = array('$GPGGA');
if(in_array($lineTokens[0],$searchCriteria2)) {
if (fwrite($outFile2,$line)===FALSE){
echo "Problem writing to file\n";
}
}
}
}
echo "<p>For the file ".$inFileName." read ".$numLines;
//close the in files
fclose($_FILES['uploadedFile']['tmp_name']);
fflush($outFile);
fflush($outFile2);
}
fclose($outFile);
fclose($outFile2);
}
?>
Try this upload class instead and see if it helps:
To use it simply Upload::files('/to/this/directory/');
It returns an array of file names that where uploaded. (it may rename the file if it already exists in the upload directory)
class Upload {
public static function file($file, $directory) {
if (!is_dir($directory)) {
if (!#mkdir($directory)) {
throw new Exception('Upload directory does not exists and could not be created');
}
if (!#chmod($directory, 0777)) {
throw new Exception('Could not modify upload directory permissions');
}
}
if ($file['error'] != 0) {
throw new Exception('Error uploading file: '.$file['error']);
}
$file_name = $directory.$file['name'];
$i = 2;
while (file_exists($file_name)) {
$parts = explode('.', $file['name']);
$parts[0] .= '('.$i.')';
$new_file_name = $directory.implode('.', $parts);
if (!file_exists($new_file_name)) {
$file_name = $new_file_name;
}
$i++;
}
if (!#move_uploaded_file($file['tmp_name'], $file_name)) {
throw new Exception('Could not move uploaded file ('.$file['tmp_name'].') to: '.$file_name);
}
if (!#chmod($file_name, 0777)) {
throw new Exception('Could not modify uploaded file ('.$file_name.') permissions');
}
return $file_name;
}
public static function files($directory) {
if (sizeof($_FILES) > 0) {
$uploads = array();
foreach ($_FILES as $file) {
if (!is_uploaded_file($file['tmp_name'])) {
continue;
}
$file_name = static::file($file, $directory);
array_push($uploads, $file_name);
}
return $uploads;
}
return null;
}
}

Categories