After hours of trying to figure out why php "ZipArchive" does not work as expected, due to Mac OS adding a "__MACOSX" folder in the zip compression process.
How would I go about deleting "__MACOSX" folder in a zip archive during the upload process in a form?
Below is what I'm working with:
<?php
public function uploadZip($file)
{
$advert_index;
$zip = new \ZipArchive;
if (true === $zip->open($file['tmp_name'])) {
$source = trim($zip->getNameIndex(0), '/');
for ($i = 0; $i < $zip->numFiles; $i++) {
$name = $zip->getNameIndex($i);
// Determine output filename (removing the `$source` prefix).
$substring_name = substr($name, strlen($source)+1);
$file = $this->option['uploads_path'] . $this->option['advert_id'] . '/' . $substring_name;
// Store the adverts `index.html` file URL to be returned.
if ('html' === pathinfo($name, PATHINFO_EXTENSION)) {
$advert_index = basename($name);
}
// Create the directories if necessary.
$dir = dirname($file);
if (! is_dir($dir)) {
mkdir($dir, 0777, true);
}
// Read from Zip and write to disk.
$fpr = $zip->getStream($name);
$fpw = fopen($file, 'w');
while ($data = fread($fpr, 1024)) {
fwrite($fpw, $data);
}
fclose($fpr);
fclose($fpw);
}
$zip->close();
return array(
'status' => true,
'message' => array(
'index' => $advert_index,
'type' => 'text/html', // #HACK: Set MIME type manually. #TODO: Read MIME type from file.
),
);
}
return array(
'status' => false,
'message' => 'Upload failed',
);
}
Any help would be much appreciated :)
Note: I Managed to find the file just can't get it to be removed.
// Check to see the __MACOSX
if($zip->getNameIndex($i) === "__MACOSX/") {
error_log($zip->getNameIndex($i) . ' - Error here continue');
$zip->deleteName("__MACOSX/");
continue; // Move on to the next iteration
// $zip->deleteIndex($i);
} else {
$name = $zip->getNameIndex($i);
}
It was actually very simple, instead to trying to delete the folder within the zip-archive just had to skip all indexes aka filename that begin with a certain string while looping through the files.
if(substr($zip->getNameIndex($i), 0, 9) === "__MACOSX/") {
continue;
} else {
$name = $zip->getNameIndex($i);
}
My zip file name is Product_Catalog.txt.gz. this zip file contain one txt file.
how can i extract and store into my database
i have already done in zip file unzip and store into my database. but i can't understand .gz format. so please advise
my zip file code is here
if (realpath($destinationname."/".$filename)){
if ($zip = zip_open(realpath($directory."/".$filename)))
{
while (($zip_entry = zip_read($zip))){
$zipfilename=zip_entry_name($zip_entry );
$zipfilename."<br>";
$tmpfname = tempnam("/tmp", "");
$handle = fopen($tmpfname, "w+");
while($data = zip_entry_read($zip_entry,50000000000)){
fwrite($handle,$data);
}// end while $data
fseek($handle,0);
if ($separatetables);
$table=strtok($zipfilename,"-");
$sql="CREATE TABLE IF NOT EXISTS `$table` LIKE `cjfeeds`";
mysql_query($sql);
how can i convert this code into txt.gz
gzip, which is generally used for single files, is not the same file format as ZIP. Although the same compression algorithm (DEFLATE) is used in both formats, the headers are entirely different, so PHP's Zip functions will not recognize your file.
Instead, you can use the compress.zlib:// wrapper to open gzip-compressed files. You can then use the normal stream functions to read the file.
$handle = fopen("compress.zlib://$filename", 'r');
However, there are some limitations; for example, opening a gzipped file in read-write mode is not possible, and seeking may be slow. If necessary, you can work around these by making a temporary uncompressed copy:
copy("compress.zlib://$filename", $tmpfname);
You don't extract.
if ($rrpath){
if ($zip = gzopen($rrpath, "rb"))
{
$filenamen = $_SESSION['files_list'][$i];
$zipfilename = str_replace('.gz', '', $_SESSION['files_list'][$i]);
$tmpfname = tempnam("/home/demoosiz/tmp", "");
$handle = fopen($tmpfname, "w+");
ini_set("max_execution_time",3000000000000);
while($data = gzread($zip, 4096)){
fwrite($handle,$data);
}// end while $data
fseek($handle,0);
if ($separatetables);
$table=strtok($zipfilename,"-");
// $sqly = "TRUNCATE TABLE `cjfeeds`";
//mysql_query($sqly);
// I'm not too sure if this is windows specific
$tmpfile=addslashes($tmpfname);
//if the script times out uncomment the next line
ini_set("max_execution_time",3000000000000);
$sql22 ="LOAD DATA LOCAL INFILE '$tmpfile' REPLACE INTO TABLE `cjfeeds` FIELDS TERMINATED BY '$fieldseparator' IGNORE 1 LINES;";
You can use this code. This code is very helpful for you.
You keep calling it a zip file. It's not a zip file (.zip). It's a gzip file (.gz). Different format.
You can use gzdecode() to decompress a gzip file.
You can interact with .gz files through the functions of the zlib module.
Responding to your comment:
You won't need to do the while($zip_entry = zip_read($zip)) since gz files don't hold multiple files within them. A .gz file is just a single file that has been compressed. Unfortunately, this also means that there is no equivalent to zip_entry_name either, since there is only one file.
If you're trying to read a tarball - an archive of files which have been concatenated together and gzipped to form a .tar.gz file - then Zlib isn't for you, since it's not designed to handle that. You'll want something like PharData instead:
$phar = new PharData($filename);
foreach ($phar as $phar_stream) {
$file_data = file_get_contents($phar_stream);
// process $file_data how you like
}
If these are just single-file gz files, then you can read the files with the gz versions of the usual filesystem functions.
You can use gzread to process chunks of the file the way you're currently doing with zip_entry_read, or you can read all lines into an array with gzfile. There is a readgzfile function which grabs the whole file, but unfortunately it seems like it just dumps the output directly to the client, rather than do a file. You could use output buffering to capture that output, but it seems like too much of a hassle considering your other options.
Try this....
if(isset($_FILES['zipfile']))
{
$filename = $_FILES['zipfile']['name'];
$source = $_FILES['zipfile']['tmp_name'];
$type = $_FILES['zipfile']['type'];
/*$name = explode('.zip', $filename); # $name[0] returns the name of the file. $name[1] returns the extension (zip)
; */ # Where the file will be saved. I.E. 'extracted/myFile-02151985/'
$target = PHYSICAL_PATH."upload/";
// Ensures that the correct file type was chosen.
$accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed');
foreach($accepted_types as $mime_type)
{
if($mime_type == $type)
{
$okay = true;
break;
}
}
$okay = strtolower($name[1]) == 'zip' ? true : false;
if(!$okay)
{
$msg="Please choose a zip file, dummy!";
}
$saved_file_location = $target.$filename;
if(move_uploaded_file($source, $target . $filename))
{
global $target;
global $unique_folder;
$zip = new ZipArchive();
$x = $zip->open($saved_file_location);
if ($x === true)
{
$folder=$zip->extractTo($target);
for ( $i=0; $i < $zip->numFiles; $i++ )
{
echo $entry = $zip->getNameIndex($i);
$filename=explode('/',$entry);
print_r($filename);
if($filename[1] != '')
{
$filename_folder=$filename[0];
$filename = $filename[1]; //
$name = explode('.zip', $filename_folder); # $name[0] returns the name of the file. $name[1] returns the extension (zip)
//echo 'sfkdf'.$name[0];
$target = PHYSICAL_PATH."upload/";
$sql = "select `id` from `tracks` order by `id` desc";
$list = mysql_query($sql);
$list_num = mysql_num_rows($list);
if($list_num > 0)
{
$res = mysql_fetch_array($list);
$new_id=$res['id'];
} else {
$new_id=1;
}
$check_for_jpg = strpos($filename, '.mp3');
if ($check_for_jpg === false ){
echo 'Check file format.';
$source=$target . $name[0].'/'.$filename;
unlink($source);
} else {
$srch = array(' ','--','"','!','#','#','$','%','^','&','*','(',')','_','+','{','}','|',':','"','<','>','?','[',']','\\',';',"'",',','/','*','+','~','`','=');
$rep = array('_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_');
$name_song[$i]= str_replace($srch,$rep,$filename);
$title_name=explode('.',$filename);
$new_id=$new_id+1;
$new_filename=$new_id.'-'.$name_song[$i];
mysql_query("insert into `tracks` set track='".addslashes(stripslashes($new_filename))."',title='".addslashes(stripslashes($title_name[0]))."',tape_id='".$id_last."',addDate=NOW(),status=1 ");
$source=$target . $name[0].'/'.$filename; $target_move=$target.$new_filename;
if(rename($source,$target_move)) { unlink($source); } else { echo 'not';}
echo 'folder zip';
echo '<li>' . $new_filename . '</li>';
}
rmdir($target . $name[0]);
}
else { echo $target = PHYSICAL_PATH."upload/";
$sql = "select `id` from `test_table` order by `id` desc";
$list = mysql_query($sql);
$list_num = mysql_num_rows($list);
if($list_num > 0) {
//and, instead of using "while":
$res = mysql_fetch_array($list); // will return the highest "id" number
echo $new_id=$res['id'];
} else {
$new_id=1;
}
$check_for_jpg = strpos($entry, '.mp3');
if ($check_for_jpg === false ){
echo 'Check file format.';
$source=$target .$entry;
unlink($source);
} else {
$srch = array(' ','--','"','!','#','#','$','%','^','&','*','(',')','_','+','{','}','|',':','"','<','>','?','[',']','\\',';',"'",',','/','*','+','~','`','=');
$rep = array('_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_','_');
$name_song[$i]= str_replace($srch,$rep,$entry);
$title_name=explode('.',$entry);
$new_id=$new_id+1;
$new_filename=$new_id.'-'.$name_song[$i];
mysql_query("insert into `tracks` set track='".addslashes(stripslashes($new_filename))."',title='".addslashes(stripslashes($title_name[0]))."',tape_id='".$id_last."',status=1 ");
$source=$target .$entry; $target_move=$target.$new_filename;
if(rename($source,$target_move)) { unlink($source); } else { echo 'not';}
echo 'only zip';
echo '<li>' . $new_filename . '</li>';
}
}
}
$zip->close();
unlink($saved_file_location); #deletes the zip file. We no longer need it.
} else {
die("There was a problem. Please try again!");
}
} else {
$msg="There was a problem";
}
}
i have an issue with uploading multiple files to disk. here is my code.
i have a request with 2 pictures that gets sent to a upload function. the 2 pictures are in a var called $multiUpload
$folderPath = '/var/www/';
if (is_array($multiUpload)){
$file = array();
$filename = array();
foreach($multiUpload as $key=>$val){
// get the file extension
$file[] = explode('.',$val);
// create custom file name
$filename[] = time().'.'.$file[$key][1];
//send to the upload function
$this->uploadToDisk($folderPath, $filename[$key]);
// sleep 1 sec so that the pic names will be different
sleep(1);
}
return $filename;
}
public function uploadToDisk($folderPath, $filename)
{
$adapter = new Zend_File_Transfer_Adapter_Http();
$adapter->setDestination($folderPath);
$adapter->addFilter( 'Rename',array(
'target' => $folderPath."/".$filename,
'overwrite' => true
) );
if ($adapter->receive()) {
$message = "success";
} else {
$message = "fail";
}
return $message;
}
this will return
Array
(
[0] => Array
(
[0] => 1332977938.jpg
[1] => 1332977939.jpg
)
)
but only array[0][0] or 1332977938.jpg will actually get saves to the disk.
Why are they now both get saved? wired
any ideas?
I suspect the second call to uploadToDisk is returning fail because you can only call Zend_File_Transfer_Adapter_Http::receive() once for each file. Since you are not specifying a file when calling receive, it is receiving all of the files the first time you call uploadToDisk and subsequently is failing with a File Upload Attack error.
Here is some code you can try. This tries to receive each file individually and then save them one at a time with each call to uploadToDisk.
A few notes about the code:
The first parameter to uploadToDisk ($val) may need to be changed as I am not sure what the original values are. It should correspond to one of the element names used for the file upload (See Zend_File_Transfer_Adapter_Http::getFileInfo()) for a list of the files.
I changed the method for generating a unique filename so you don't have to sleep(1)
Zend_File_Transfer_Adapter_Abstract::setDestination() is deprecated and will go away in the future. Instead, just use the Rename filter. When using Rename, setDestination() has no effect.
And here it is...
<?php
$folderPath = '/var/www/';
if (is_array($multiUpload)){
$filenames = array();
foreach($multiUpload as $key => $val){
// get the file extension
$ext = explode('.', $val);
$ext = $ext[sizeof($ext) - 1];
// create custom file name
do {
$filename = uniqid(time()) . '.' . $ext;
$diskPath = $folderPath . $filename;
} while (file_exists($diskPath));
$filenames[$key] = $filename;
//send to the upload function
// $val is the file to receive, $diskPath is where it will be moved to
$this->uploadToDisk($val, $diskPath);
}
return $filename;
}
public function uploadToDisk($file, $filename)
{
// create the transfer adapter
// note that setDestination is deprecated, instead use the Rename filter
$adapter = new Zend_File_Transfer_Adapter_Http();
$adapter->addFilter('Rename', array(
'target' => $filename,
'overwrite' => true
));
// try to receive one file
if ($adapter->receive($file)) {
$message = "success";
} else {
$message = "fail";
}
return $message;
}
I am using PHPExcel to read data out of Excel files.
With the following code, I am able to read one particular worksheet out of a 3MB Excel file in just a couple seconds. Works nicely.
However, I now have 27MB and 88MB Excel files which I need to get data out of. They are so large that even OpenOffice cannot open them.
I have found that I can use an index number instead of a name when I load a sheet, but this seems inconsistent, e.g. in one particular Excel file setLoadSheetsOnly(0) gave me the third sheet while setLoadSheetsOnly(1) gave me an error even though there were four worksheets in the file. So that seems unreliable for some reason.
Is there a way I can read out the names of the worksheets from a large file so that I can access only one of its worksheets at a time?
$objReader = PHPExcel_IOFactory::createReaderForFile("data/" . $file_name);
$objReader->setLoadSheetsOnly(array($sheet_name));
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load("data/" . $file_name);
echo '<table border="1">';
for ($row = 1; $row < $number_of_rows; $row++) {
echo '<tr>';
for ($column = 0; $column < $number_of_columns; $column++) {
$value = $objPHPExcel->setActiveSheetIndex(0)->getCellByColumnAndRow($column, $row)->getValue();
echo '<td>';
echo $value . ' ';
echo '</td>';
}
echo '</tr>';
}
echo '</table>';
die;
ADDENDUM:
I found some code that gets close, but it doesn't seem to always be accurate, e.g. here it missed the second worksheet in a 27MB file:
and here it only got the third worksheet and missed 3 others:
$objReader = PHPExcel_IOFactory::createReaderForFile("data/" . $file_name);
$objReader->setLoadSheetsOnly(0);
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load("data/" . $file_name);
echo $objPHPExcel->getSheetCount(), ' worksheets<hr/>';
$loadedSheetNames = $objPHPExcel->getSheetNames();
foreach ($loadedSheetNames as $sheetIndex => $loadedSheetName) {
echo $sheetIndex, ' -> ', $loadedSheetName, '<br />';
}
die;
Unfortunately, it isn't possible to read the names of the worksheets without loading the whole file.
Using an index number rather than a name when calling setLoadSheetsOnly() won't give a predictable result: the code logic that performs that check uses in_array() to test if the sheetname that it is about to read is in the array of sheetnames to read. e.g.
// check if sheet should be skipped
if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
continue;
}
I would suspect that the comparison of a string against a numeric value will be giving a true result for 0 == "mySheetName" when executing this test (based on PHP's loose typing and comparison casting rules).
I could probably provide a Reader method that would return a list of worksheet names without actually loading the entire file, although there would be a performance hit involved.
EDIT
If you add the following method to Classes/PHPExcel/Reader/Excel2007.php
/**
* Reads names of the worksheets from a file, without loading the whole file to a PHPExcel object
*
* #param string $pFilename
* #throws Exception
*/
public function listWorksheetNames($pFilename)
{
// Check if file exists
if (!file_exists($pFilename)) {
throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
}
$worksheetNames = array();
$zip = new ZipArchive;
$zip->open($pFilename);
$rels = simplexml_load_string($this->_getFromZipArchive($zip, "_rels/.rels")); //~ http://schemas.openxmlformats.org/package/2006/relationships");
foreach ($rels->Relationship as $rel) {
switch ($rel["Type"]) {
case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument":
$xmlWorkbook = simplexml_load_string($this->_getFromZipArchive($zip, "{$rel['Target']}")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
if ($xmlWorkbook->sheets) {
foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
// Check if sheet should be skipped
$worksheetNames[] = (string) $eleSheet["name"];
}
}
}
}
$zip->close();
return $worksheetNames;
}
You can call it by using:
$inputFileType = 'Excel2007';
$inputFileName = 'biostat-behfisk-2005.xlsx';
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
$worksheetNames = $objReader->listWorksheetNames($inputFileName);
foreach ($worksheetNames as $sheetName) {
echo $sheetName, '<br />';
}
the returned $worksheetNames should contain an array of all worksheet names as UTF-8 strings. Because it's only reading the absolute minimum from the .xlsx to retrieve these names, it should be fairly fast. I'll do some more tests before checking it into the PHPExcel SVN, but (for now) it seems to do what you need.
EDIT2
Equivalent method for the Excel5 Reader
/**
* Reads names of the worksheets from a file, without loading the whole file to a PHPExcel object
*
* #param string $pFilename
* #throws Exception
*/
public function listWorksheetNames($pFilename)
{
// Check if file exists
if (!file_exists($pFilename)) {
throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
}
$worksheetNames = array();
// Read the OLE file
$this->_loadOLE($pFilename);
// total byte size of Excel data (workbook global substream + sheet substreams)
$this->_dataSize = strlen($this->_data);
$this->_pos = 0;
$this->_sheets = array();
// Parse Workbook Global Substream
while ($this->_pos < $this->_dataSize) {
$code = self::_GetInt2d($this->_data, $this->_pos);
switch ($code) {
case self::XLS_Type_BOF: $this->_readBof(); break;
case self::XLS_Type_SHEET: $this->_readSheet(); break;
case self::XLS_Type_EOF: $this->_readDefault(); break 2;
default: $this->_readDefault(); break;
}
}
foreach ($this->_sheets as $sheet) {
if ($sheet['sheetType'] != 0x00) {
// 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
continue;
}
$worksheetNames[] = $sheet['name'];
}
return $worksheetNames;
}
Not as efficient as the Excel2007 Reader version, but should still be faster than parsing the whole .xls file just for the sheet names because I'm only parsing the global stream.
I didn't want to amend phpexcel so I went with this:
public function getWorksheetNames($pFilename) {
$worksheetNames = array ();
$zip = zip_open ( $pFilename );
while ( $entry = zip_read ( $zip ) ) {
$entry_name = zip_entry_name ( $entry );
if ($entry_name == 'xl/workbook.xml') {
if (zip_entry_open ( $zip, $entry, "r" )) {
$buf = zip_entry_read ( $entry, zip_entry_filesize ( $entry ) );
$workbook = simplexml_load_string ( $buf );
foreach ( $workbook->sheets as $sheets ) {
foreach( $sheets as $sheet) {
$attributes=$sheet->attributes();
$worksheetNames[]=$attributes['name'];
}
}
zip_entry_close ( $entry );
}
break;
}
}
zip_close ( $zip );
return $worksheetNames;
}
It only works on excel 2007 or later but did what I needed