How to dynamically present an on the fly zipfile to users - php

I'm editing an ancient script that zips several images and presents them dynamically to the user.
I have rewritten almost all of the code, but I can't find a way to way to output the contents of the zipfile. Writing it to the server is very undesirable.
I create the file with:
$z = new ZipArchive();
I can add content with:
$z->addFromString("filename",$string);
And I want to present it dynamically with:
header("Content-Type: application/zip;");
header("Content-Disposition: attachment; filename=file.zip;");
// I need a function to read the contents of the zipfile here. Something like:
echo $z->filecontent();
I can't find out what function to use for this.

You would open the file, creating it most likely with temp name. Something like this:
$name = tempnam('/tmp','zip');
$z->open($name, ZIPARCHIVE::CREATE)
After you finish adding all your files, you would close it.
$z->close();
Now when you are ready to send the data you would do this:
readfile($name);
After you are done, you want to clean up the temp file with:
unlink($name);

If you read the documentation, look at the close() method to actually save the file physically to the filesystem. Then you can use readfile() on the saved file

Related

Gather files attachments sent by another PHP script, and output them as a ZIP folder

I currently have a PHP script that exports data about an invoice in a CSV file, based on the invoice's ID number. The file is dynamically generated and is not saved on the server. To be more specific, I am echoing rows, and setting the following headers so that the echos appear in a file attachment:
header('Content-type: text/plain');
header("Content-Disposition: attachment; filename=INV${id}.csv");
The way I can call the script is using:
my_script.php?id=123
However now, I need to write a script that grabs all invoices of a given day, get their corresponding CSV files, and download them to the client as a ZIP file. Can I do that using my already existing script that outputs one file?
What I was thinking about is getting the list of all relevant invoice IDs from the database (say 123, 456 and 789), then looping over them and call my script for each of the IDs, like:
my_script.php?id=123 //then 456, then 789
and then save the file results into an array.
My attempt so far:
So far, I tried saving one resulting file as a variable as per this answer, but the variable remains empty (while the already existing script does return a non-empty file):
$file = getScriptOutput("my_script.php?id=123");
echo $file;
function getScriptOutput($path, $print = FALSE)
{
ob_start();
if( is_readable($path) && $path )
{
include $path;
}
else
{
return FALSE;
}
if( $print == FALSE )
return ob_get_clean();
else
echo ob_get_clean();
}
So, two questions here:
1 - How do I make an array of files, where each one is a file result (attachment) of a PHP script?
2 - How do I combine all these dynamic files into one ZIP file in PHP?
Edit: The suggested duplicate does help for the zipping part, but I need to know how to get the file attachment of a PHP script (Is there a way without saving the file on the server, because of storage and security concerns?).
You could just save all your files in some location, then access them and put them into a zip file like this:
$zip = new ZipArchive;
if ($zip->open('test_new.zip', ZipArchive::CREATE) === TRUE)
{
// Add files to the zip file
$zip->addFile('test.txt');
$zip->addFile('test.pdf');
// All files are added, so close the zip file.
$zip->close();
}
Well, I rethought my plan, and I finally went for the following:
For each of the IDs:
Read the attachment of the http response coming from my_script.php?id=123 and saving it into a variable as per this answer (using cURL and setting CURLOPT_BINARYTRANSFER => TRUE)
Create a temporary file and writing in it the varaible's data (typical fopen and fwrite did the job)
Add the file to the ZIP archive, as per #DigitalJedi's answer
After sending the ZIP, delete the temporary file using unlink("filename.csv").

PHP link/request to download file then delete it immediately

I face a case I never did, and I dont know how to properly do it.
I have a php script which generate files for clients. At the end of the script, I echo the path for them to download the file, simply.
How can I do to provide the file - or the path or any what - for downloading it, and be sure to delete the file once downloaded.
Widely, I'd like to make the file available for one/unique download only. How to ?
EDIT
I cannot use headers
There are a few components to getting this to work. Without knowing which framework you use, I'll use comments as placeholders.
There is no way to do it without using the header function, though.
Here is the source for a file that outlines the process:
<?php
$fileid = $_GET['fileid'];
$key = $_GET['key'];
// find the file in the database, and store it in $file
if ($keyMatches) {
// it is important for security to only use file paths from the database
$actualPath = $file->getPathOnDisk();
$fileInfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($fileInfo, $actualPath);
$fp = fopen($actualPath, 'rb');
header("Content-Type: " . $mime);
header("Content-Length: " . filesize($actualPath));
fpassthru($fp);
}
else
{
http_response_code(403); // forbidden
}
You'll use this by linking to download.php?fileid=1234&key=foobar, and generating the URL at the same time you generate the key and store it in the database.
For security, you'll keep the files outside of the web root, meaning they cannot be accessed through the web server without going through a script.
fpassthru is reasonably fast, and will not likely have a performance impact.
You must do a download file gateway, like download.php?id=XXX
Where XXX is the unique ID of each file you will store in DB. And of course, the file to be downloaded.
Then, each time a user will visit the page, you can :
- Check if he has already downloaded the file
- If no, redirect it to the real path of file
- If yes, display 403 message.
When a user download a file, update the DB, generate or copy the file to a new name, you play with headers, and delete file upon download or after a small timeout.

php get file content of downloadable file

Coult not find any similar problem solved on the web, so here's my situation:
I have a .jsp "webpage" that generates a .csv file based on specific parameters.
As an example, if I use my browser to open the site, I type in:
redownloadsubmitter.jsp?id=225&batch_id=2013_11_20&orgshort=NEP
The script then uses the data in the query string and generates the matching .csv file, named: NEP_DETAILS_2013_11_20.csv
Now what I want is to not manually having to use my browser, open the script and download the file to my local harddrive. Instead I want to use a PHP script that grabs the content and then can further format it, based on my needs.
I thought about the following code, but that did not work. Instead it returns nothing, empty website when I try it..
$download = file_get_contents('redownloadsubmitter.jsp?id=225&batch_id=2013_11_20&orgshort=NEP');
echo $download;
Any other ideas?
NOTE: just in case someone has this question: I have no access to the .jsp file and I therefore cannot change how it operates.
file_get_contents() isn't smart and doesn't know that's a URL you're passing in. It's trying to literally open a local file whose name is redownloadsubmitted.jsp.etc......
If you want f_g_c() to do an HTTP operation, then you'll have to include a full-blown URL:
$download = file_get_contents('http://example.com/redownloadsubmitter.jsp etc....');'
Try this code for download file.
<?php
/**
* $filename filename in server
* $downloadname filename when download file
*/
$filename = __FILE__;
$dowloadname = 'PHPDownload.php';
Header("content-type:application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Accept-Length: ".filesize($filename));
Header("Content-Disposition: attachment; filename=".$dowloadname);
if(file_exists($filename) && $fp=fopen($filename,"r")) //file exists and open it
{
echo fread($fp,filesize($filename)); //read write to the browser
fclose($fp);
}
//End_php

I want to search a directory for multiple files using PHP and start a download them over HTTP

Just to start, I'm a PHP noob.
I have an Apache server which hosts my files. I have a device which can only point to one PHP file. What I need it to do is have my PHP file read in the name of the file I want to download, and point it towards the directory it is stored. Currently, I have it pointing to one file, but I need it to be able to point to multiple. Is this possible in PHP?
Here's what I have so far:
<?php
$file_name = 'file.img';
$size = filesize($file_name);
$file_url = 'http://192.168.0.5/' . $file_name;
header("Content-length: $size");
header("Content-Type: text/plain");
header("Content-Transfer-Encoding: binary");
header("Content-disposition: attachment; filename=\"".$file_name."\"");
readfile($file_url);?>
Edit:
The commands I want to input in order to download the file are close enough to as follows:
cmd=download+-a+$$.img+altimage
cmd=download+-a+$$.conf+altconfig
and the download directory is the .php file. I am open to other suggestions in how to do this.
Edit2:
Here's what an exact sample URL is:
myserver.com/cgi-bin/va/cmd?hdl+fullconfig.ini+altconfig
the hdl is a predefined function which points to the download directory, in order to download the file from the server, so the layout of what you mean isn't exactly the same.
I have trouble understanding what exactly you're trying to do, but I guess that you want a user to be able to download multiple files. If that is correct, here is one way to achieve this:
You can let PHP create a ZIP archive using the ZIP extension. For it to work, you have to load the extension php_zip.dll inside your php.ini.
$ZIP = new ZipArchive();
// Use the current time as the filename to prevent two users to use the same file
$ZIPName = microtime().".zip";
// Create a new ZIP file
$ZIP->open($ZIPName, ZIPARCHIVE::CREATE);
// Loop through all files - $Files needs to be an array with the names of the files you want to add, including the paths
// basename() will prevent the creation of folders inside the ZIP
foreach ($Files as $File) {
$ZIP->addFile($File, basename($File));
}
// Close the archive
$ZIP->close();
// Send the archive to the browser
readfile($ZIPName);
I hope this is what you were looking for.

How to get a temporary file path?

I know you can create a temporary file with tmpfile and than write to it, and close it when it is not needed anymore. But the problem I have is that I need the absolute path to the file like this:
"/var/www/html/lolo/myfile.xml"
Can I somehow get the path, even with some other function or trick?
EDIT:
I want to be able to download the file from the database, but without
$fh = fopen("/var/www/html/myfile.xml", 'w') or die("no no");
fwrite($fh, $fileData);
fclose($fh);
because if I do it like this, there is a chance of overlapping, if more people try to download the same file at exactly the same time. Or am I wrong?
EDIT2:
Maybe I can just generate unique(uniqID) filenames like that, and than delete them. Or can this be too consuming for the server if many people are downloading?
There are many ways you can achieve this, here is one
<?php
// Create a temp file in the temporary
// files directory using sys_get_temp_dir()
$temp_file = tempnam(sys_get_temp_dir(), 'MyFileName');
echo $temp_file;
?>
The above example will output something similar to:
/var/tmp/MyFileNameX322.tmp
I know you can create a temporary file with tmpfile
That is a good start, something like this will do:
$fileHandleResource = tmpfile();
Can I somehow get the path, even with some other function or trick?
Yes:
$metaData = stream_get_meta_data($fileHandleResource);
$filepath = $metaData['uri'];
This approach has the benefit of leaving it up to PHP to pick a good place and name for this temporary file, which could end up being a good thing or a bad thing depending on your needs. But it is the simplest way to do this if you don't yet have a specific reason to pick your own directory and filename.
References:
http://us.php.net/manual/en/function.stream-get-meta-data.php
Getting filename (or deleting file) using file handle
This will give you the directory. I guess after that you are on your own.
For newer (not very new lol) versions of PHP (requires php 5.2.1 or higher) #whik's answer is better suited:
<?php
// Create a temp file in the temporary
// files directory using sys_get_temp_dir()
$temp_file = tempnam(sys_get_temp_dir(), 'MyFileName');
echo $temp_file;
?>
The above example will output something similar to: /var/tmp/MyFileNameX322.tmp
old answer
Just in case someone encounters exactly the same problem. I ended up doing
$fh = fopen($filepath, 'w') or die("Can't open file $name for writing temporary stuff.");
fwrite($fh, $fileData);
fclose($fh);
and
unlink($filepath);
at the end when file is not needed anymore.
Before that, I generated filename like that:
$r = rand();
$filepath = "/var/www/html/someDirectory/$name.$r.xml";
I just generated a temporary file, deleted it, and created a folder with the same name
$tempFolder = tempnam(sys_get_temp_dir(), 'MyFileName');
unlink($tempFolder);
mkdir($tempFolder);

Categories