Creating zip archive makes multiple files appended with unpredictable strings - php

I'm trying to make a PDf downloader where the user can select a couple files and then download a zip of what they selected. I had it working on my personal server, but on the production server I get an odd error. The zip file is generated, but it's appended with a string of numbers eg;
zipfile.zip.a08752, zipfile.zip.b08752
Weirder still, if I delete the string off the end and download the file it expands properly.
I read in this topic PHP Zip Archive sporadically creating multiple files that it's an issue with the file attempting to close multiple times, failing and the retrying.
Heres the code for my zip function, though I suspect it's something to do with the configuration of the
function buildZip($params){
/* Generate unique Id */
$downloadid = uniqid();
/* Pull in the order.xml */
if(!empty($_REQUEST['downloadlink'])){
if( $params->usexml == true){
$xml = #simplexml_load_file($params->pdfolder.'/order.xml');
$order = $xml->children();
}else{
$order = $params->files;
}
/* Create the new Zip */
$zip = new ZipArchive();
if ($zip->open($params->zipname.'version'.$downloadid.'.zip', ZIPARCHIVE::CREATE) !== TRUE) {
die ("Could not open archive");
}
/* Generate the download link to output further down the page */
global $downloadLink;
$downloadLink = $params->zipname.'version'.$downloadid.'.zip';
/* Make selected variable available to build the listSelection function */
global $selected;
$selected = array();
$i = 0;
foreach($order as $el){
if (isset($_POST[$i]) == true){
//generate list of selected PDF's
array_push($selected, $el->name);
//grab selected pdf's and zip them.
echo $zip->addFile($params->pdfolder.'/'.$el->link);
$zip->addFile($params->pdfolder.'/'.$el->link) or die ("ERROR: Could not add file: pdf'.$i.'.html");
}
$i++;
}
$zip->close();
}
}
<code>
For Clarity, I'm pulling in an XML list called order.xml to pull in the array of possible files.

Try trimming the weird string of numbers after zipping is done

I just ran into the same problem. This is happening because the zip file fails to get created. It's failing because your files are too large. To fix the problem, I had to create multiple smaller zip files. I hope this helps.

Related

Reading filenames from a zip file inside another zip file

I am trying to get all filenames from the files inside a zip file. It all works perfectly until the zip file contains another zip file and I try to get the included file names from the included zip as well.
I want to get all included filenames without extracting the file.
For some reason it always refuses to open the included zip file, as if it does not recognie it as a zip file.
To make 100% sure it is a zip for testing I simply included the same zip inside the main zip.
While it properly reads the main zipfile it returns false if I want to read the included file.
I have been trying to get this script to work for 3 days now but I keep failing so I decided to see if someone here can help me out with this.
This is the script I am using to read a zipfile contents:
function firstzipper($file) {
global $filesroot, $pagefile;
$zipinc = new ZipArchive();
if ($zipinc->open($file) === TRUE) {
for ($i = 0; $i < $zipinc->numFiles; $i++) {
$filename_full = $zipinc->getNameIndex($i);
$filename = substr($filename_full, strrpos($filename_full, '/') + 1);
if (!is_dir($filename)) {
$filename = filter_var($filename, FILTER_SANITIZE_STRING);
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if ($ext == 'zip') {
secondzipper($filesroot.$pagefile.'/'.$filename_full);
}
}
array_push($inc_files_arr,$filename);
}
return $inc_files_arr;
}
}
firstzipper($filesroot.$url);
secondzipper inside the function above is simply the same function, copied and renamed.
The zipfile looks like this:
E:/myfolder/fileserver/temper.zip/myincluded.zip
The function opens temper.zip but returns false on myincluded.zip.
I hope I have included all info needed, if not let me know and I add what else is required.

Inspect file in zip archive without extracting

I wrote a short code, which opens up a Zip File and searches for a file called "index.html".
Now I want to open the file and perform several actions.
- Search for links.
- Search for clicktags.
Please keep in mind, that this is done while the user is uploading his file.
I dont want to extract is somewhere on the server.
Is there a good method to achieve this?
Regards
$zip = new ZipArchive();
$zip -> open($filepath);
//Assign filescount to variable
$this ->adFileCount = $zip -> numFiles;
//Scan all files and find index.html
if($zip ->getFromName("index.html") == true)
{
//specific action done with index.html
}
Read the contents of the file and do whatever you need to with it.
$zip -> open($filepath);
for($floop = 0; $floop < $zip->numFiles; $floop++ ) {
$stat = $zip->statIndex($floop);
if (stripos($stat['name'],'index.html') !== false) {
$indexcontents = $zip->getFromIndex($floop);
//
// do whatever you need to do with the array
// named indexcontents that contains index.html
//
}
} // end of for loop through the files in the zipped file uploaded
$zip->close();

PHP best way to call loop function multiple time

I have a specific directory which may contain zip files.
I would like to loop through each sub-element of my directory to check if this is a zip. And unzip that. Then process the others files.
I'm using flysystem to work with my files.
So I went for this
$contents = $this->manager->listContents('local://my_directory , true);
foreach ($contents as $file) {
if( $file['extension'] == 'zip')
//Unzip in same location
}
The problem is that the files unziped are not in the loop and if the zip file, contain another zip. The second one will be never be unziped.
So I thought about it
function loopAndUnzip(){
$contents = $this->manager->listContents('local_process://' . $dir['path'] , true);
foreach ($contents as $file) {
if( $file['extension'] == 'zip')
//Unzip and after call loopAndUnzip()
}
}
But the initial function will never be finished and be called over and over if there are zip inside zip.
Isn't it a performance issue?
How to manage this kind of thing?
You can use glob to find them, and make the function recursive. You can do this by starting at a certain dir, unzip all the files into it & check if there are new zips.
I recommend using recursive directories as well. If A.zip and B.zip both have a file called example.txt, it overwrites. With dirs it wont:
function unzipAll(string $dirToScan = "/someDir", $depth=0):void {
if($depth >10 ){
throw new Exception("Maximum zip depth reached");
}
$zipfiles = glob($dirToScan."*.zip");
// Unzip all zips found this round:
foreach ($zipfiles as $zipfile) {
$zipLocation = "/".$zipname;
// unzip here to $zipLocation
// and now check if in the zip dir there is stuff to unzip:
unzipAll($dirToScan.$zipLocation, ++$depth);
}
}
The $depth is optional, but this way you cant zipbomb yourself to death.
loopAndUnzip will do all files again, so you will just again unpack the same zipfile and start over with the entire folder, ad infinitum.
Some possibilities:
Keep a list of items that was already processed or skipped and don't process those again, so while iterating over $contents, keep a separate array, and have something like:
PHP:
foreach ($contents as $file) {
if (!array_search($processedFiles, $file) {
if( $file['extension'] == 'zip')
//Unzip in same location
}
$processedFiles[] = $file;
}
Use an unzipper that returns a list of files/folders created, so you can explicitly process those instead of the full directory contents.
If the unzipper can't do it, you could fake it by extracting to a separate location, get a listing of that location, then move all the files in the original location, and process the list you got.

php getID3 returning the same info of an updated file?

I'm using the getID3 library to get the details of a remote video file. I'm trying to read a portion of the file to get the details of the file, however some videos don't have the full details at the start.
For these videos, I'm trying to download the full video, and then extract the relevant information. However, even after the video has downloaded completely, getID3->analyze($filename), returns the same erroneous file info.
But when I copy the video, and then run the function analyze($filename.'copied.mp4') on copied video, it returns the correct info even though the file contents are same. Perhaps getID3 isn't loading the video again, however, how can I fix this issue without copying the video.
Please find the code below.
if ($fp_remote = fopen($remotefilename, 'r')) {
echo 'conn opened';
$localtempfilename = tempnam('/home/xerox/abc', 'whateva').'.mp4';
if ($fp_local = fopen($localtempfilename, 'wb')) {
$count = 0;
$countExpiry = 8;
while ($buffer = fread($fp_remote, 8192)) {
$count++;
fwrite($fp_local, $buffer);
if ($count >= $countExpiry) {
fflush($fp_local);
$getID3 = new getID3;
$ThisFileInfo = $getID3->analyze($localtempfilename);
if ($ThisFileInfo["error"]){
print "problem encouterd";
$countExpiry += 1000;
} else {
break;}
}
}
fclose($fp_local);
$getID31 = new getID3;
copy ( $localtempfilename, $localtempfilename.'_copied.mp4' );
$ThisFileInfoz = $getID31->analyze($localtempfilename.'_copied.mp4');
// Delete temporary file
unlink($localtempfilename);
fclose($fp_remote);
var_dump($ThisFileInfoz);
}
}
A call to clearstatcache solved the problem for me,
since repeated calls to things like filesize will be cached by the
filesystem and getID3 won't read beyond end-of-file.
source: James Heinrich, developer of getID3.

PHP Foreach and jQuery

I am working on a piece of code that I am wanting to "spice" up with jQuery but I can't think of a way to actually make it work. I am sure its simple, I just need a little advice to get me going.
I am wanting to create a piece of code that makes an Ajax request out to start a big loop that will download files and then upload them to an S3 bucket of mine. The place where I am stuck is I am wanting to send back a request back to the browser everytime a file is uploaded and output a string of text to the screen upon completion.
I don't have any of the frontend code working... just trying to get my head wrapped around the logic first... any ideas?
PHP Backend Code:
<?php
public function photos($city) {
if(isset($city))
$this->city_name = "{$city}";
// grab data array from Dropbox folder
$postcard_assets = $this->conn->getPostcardDirContent("{$this->city_name}", "Photos", TRUE);
$data = array();
foreach($postcard_assets['contents'] as $asset) {
//only grab contents in root folder... do not traverse into sub folders && make sure the folder is not empty
if(!$asset['is_dir'] && $asset['bytes'] > 0) {
// get information on file
$file = pathinfo($asset['path']);
// download file from Dropbox
$original_file = $this->conn->downloadFile(str_replace(" ", "%20", $asset['path']));
// create file name
$file_name = $this->cleanFileName($file['basename']);
// write photo to TMP_DIR ("/tmp/photos/") for manipulation
$fh = fopen(self::TMP_DIR . $file_name, 'w');
fwrite($fh, $original_file);
fclose($fh);
// Resize photo
$this->resize_photo($file_name);
// hash file name
$raw_file = sha1($file_name);
// create S3 hashed name
$s3_file_name = "1_{$raw_file}.{$file['extension']}";
// Upload manipulated file to S3
$this->s3->putObject($s3_file_name, file_get_contents(self::TMP_DIR . $file_name), $this->photo_s3_bucket, 'public-read');
// check to see if file exists in S3 bucket
$s3_check = $this->s3->getObjectInfo($s3_file_name, $this->photo_s3_bucket);
// if the file uploaded successully to S3, load into DB
if($s3_check['content-length'] > 0) {
$data['src'] = $s3_file_name;
$data['width'] = $this->width;
$data['height'] = $this->height;
Photo::create_postcard_photo($data, "{$this->city_name}");
// Now that the photo has been uploaded to S3 and saved in the DB, remove local file for cleanup
unlink(self::TMP_DIR . $file_name);
echo "{$file_name} uploaded to S3 and resized!<br />";
}
}
}
// after loop is complete, kill script or nasty PHP header warnings will appear
exit();
}
?>
The main problem is that with PHP, the output is buffered so it won't return a line at a time. You can try and force the flush but it's not always reliable.
You could add an entry to the DB for each file that is exchanged and create a seperate API to get the details of what has completed.
Generally, Jquery will wait till the request has finished before it allows you to manipulate data from a HTTP request.

Categories