Reputation: 1081
Udated question at bottom
I want to write a PHP script witch can send the content of given URL attached to email without downloading and uploading again.
I have limited resources to do this: normal hosting account with PHP and 64M memory limit and ~1GB storage
My first code was a simple sendmail with fopen()
, but have memory problems if the given file is too large. How can I fix this?
<?php
// return the file size of remote file
function ffilesize($remoteFile,$verbose=0){
$ch = curl_init($remoteFile);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //not necessary unless the file redirects (like the PHP example we're using here)
$data = curl_exec($ch);
curl_close($ch);
if ($data === false) {
echo 'cURL failed';
exit;
}
$contentLength = 'unknown';
$status = 'unknown';
if (preg_match('/^HTTP\/1\.[01] (\d\d\d)/', $data, $matches)) {
$status = (int)$matches[1];
}
if (preg_match('/Content-Length: (\d+)/', $data, $matches)) {
$contentLength = (int)$matches[1];
}
return ($verbose)?number_format($contentLength)." bytes":$contentLength;
}
$file_url=$_GET['q']; // the given URL
print ffilesize($file_url,1); // check the file size of given URL
if ($_GET['m']) { // allow to send mail
$filesize=ffilesize($file_url);
$to=$_GET['to']; // For the file to be sent.
$from="[email protected]"; // For the from line on the received email
$name=$_GET['fname']; // virtual name of attached file
$type="application/x-gzip";
$subject = "subj";
$mime_boundary = "==Multipart_Boundary_x".md5(mt_rand())."x";
// open the file for a binary read
$file = file_get_contents($file_url);
// read the file content into a variable
$data = chunk_split(base64_encode(file_get_contents($file_url)));
// now we encode it and split it into acceptable length lines
//ALREADY DONE, MOVE UP A FEW LINES
// message body
$message = "file attached";
// build headers
$headers = "Content-Type: multipart/mixed;\r\n" .
" boundary=\"{$mime_boundary}\"";
// put message body in mime boundries
$message = "This is a multi-part message in MIME format.\n\n" .
"--{$mime_boundary}\n" .
"Content-Type: text/plain; charset=\"iso-8859-1\"\n" .
"Content-Transfer-Encoding: 7bit\n\n" .
$message . "\n\n";
// attachment with mime babble
$message .= "--{$mime_boundary}\n" .
"Content-Type: {$type};\n" .
" name=\"{$name}\"\n" .
//"Content-Disposition: attachment;\n" .
//" filename=\"{$backfile}\"\n" .
"Content-Transfer-Encoding: base64\n\n" .
$data . "\n\n" .
"--{$mime_boundary}--\n";
// send mail
mail($to, $subject, $message, $headers);
}
?>
the problem with this method:
73,214,655 bytes Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 66846720 bytes) in /url2mail.php on line 58 the line 58 is:
$file = file_get_contents($file_url);
UPDATE A working version pasted below, but still limited by the the maximalized allowed memory. I love to make Base 64 encoded slices in memory instead of downloading file to the server.
<html><head><title></title>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
</head><body bgcolor="#000000" color="#00ff00" style="padding: 3em;margin: 0;color: #00ff00;">
<pre><?php
if(preg_match("/ ***my top secret user agent string*** /i",$_SERVER['HTTP_USER_AGENT'])){
// security check
$mem=($_GET['M'])?$_GET['M']:'32M'; // change memory limit if needed
ini_set('memory_limit', $mem);
function ffilesize($remoteFile,$verbose=0){ // return the file size of remote file
$ch = curl_init($remoteFile);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //not necessary unless the file redirects (like the PHP example we're using here)
$data = curl_exec($ch);
curl_close($ch);
if ($data === false) {
echo 'cURL failed';
exit;
}
$contentLength = 'unknown';
$status = 'unknown';
if (preg_match('/^HTTP\/1\.[01] (\d\d\d)/', $data, $matches)) {
$status = (int)$matches[1];
}
if (preg_match('/Content-Length: (\d+)/', $data, $matches)) {
$contentLength = (int)$matches[1];
}
return ($verbose)?number_format($contentLength)." bytes":$contentLength;
}
$furl=$_GET['q']; // the given URL
$fname=pathinfo(parse_url($furl, PHP_URL_PATH), PATHINFO_BASENAME); // name of given file
function return_bytes($val) { // convert size to bytes
$val = trim($val);
$last = strtolower($val[strlen($val)-1]);
switch($last) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}
return $val;
}
function mem($c=FALSE){ // checking allowed memory and used memory
$limit=return_bytes(ini_get('memory_limit'));
if ($c) {
return $limit - (memory_get_usage()*$c);
} else {
return $limit - memory_get_usage();
}
}
$filesize=(file_exists($fname))?filesize($furl):ffilesize($furl);
echo "<br /> file: ".number_format($filesize); // debug formatted file size
echo "<br /> mfree: ".number_format(mem()); // debug free memory
echo "<br /> mopt: ".number_format(mem(6)); // debug optimal memory usage
if($filesize>(mem())){ // debug file size information
echo "<br /> [".$filesize."] too large <br />";
} else {
echo "<br /> [".$filesize."] is cool <br />";
}
if ($_GET['to']) { // allow to send email
if (function_exists('curl_init')&&!file_exists($fname)) { // downloading the file
$ch = curl_init($furl);
$fh = fopen($fname, "w");
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0');
curl_setopt($ch, CURLOPT_FILE, $fh);
curl_exec($ch);
curl_close($ch);
} else {
echo 'file existing on server';
}
$furl = $fname; // redeclare the $furl to local
$to=$_GET['to']."@gmail.com"; // For the file to be sent.
$from="no-reply@".$_SERVER['SERVER_NAME']; // For the from line on the received email
$name="asd.gif"; // virtual name .gif for content protection
$type="application/x-gzip";
$subject = "subj";
$mime_boundary = "==Multipart_Boundary_x".md5($rand)."x";
// open the file for a binary read
$file = fopen($furl,'rb');
// read the file content into a variable and encode it and split it into acceptable length lines
$data = chunk_split(base64_encode(fread($file,$filesize)));
// message body
$message = $furl." (".number_format(filesize($furl)).")";
// build headers
$headers = "Content-Type: multipart/mixed;\r\n" .
" boundary=\"{$mime_boundary}\"";
// put message body in mime boundries
$message = "This is a multi-part message in MIME format.\n\n" .
"--{$mime_boundary}\n" .
"Content-Type: text/plain; charset=\"iso-8859-1\"\n" .
"Content-Transfer-Encoding: 7bit\n\n" .
$message . "\n\n";
// attachment with mime babble
$message .= "--{$mime_boundary}\n" .
"Content-Type: {$type};\n" .
" name=\"{$name}\"\n" .
"Content-Transfer-Encoding: base64\n\n" .
$data . "\n\n" .
"--{$mime_boundary}--\n";
// close the file
fclose($file);
// send mail
if (mail($to, $subject, $message, $headers)) {
echo " done";
unlink($furl);
} else {
echo $furl;
}
}
} else {
echo "unauthorized request";
}
?></body></html>
So, my question is: How can is split the given file (by URL) to slices allowed by memory limit?
in example: 32M memory limit allow me to store ~30M in memory, so i should split the 30M file to 3 part.
Upvotes: 0
Views: 2387
Reputation: 2019
I would do these steps:
1) a) Store url-s in a `mysql` table
b) if you does not have mysql than store the urls in a `csv` file.
2) - Php reads the urls
a) from the table (with `mysqli` php lib)
b) with `fgetcsv` function
- and dowload the content of the url with `curl`
- zip the content with `ZipArchive` class. /if you want to use less space and bandwith/
- Send the zipped content as attached file with `PHPMailer`
3) The 2) should run over the urls (and somehow flag what url-s are examined)
(if the runtime of the script reaches MAX_EXECUTION_TIME it is stops.
So if the script run again, it should start where it is ended at last run)
4) Setup a `cron` job to call your script as often as you need.
If you create your script like this, you dont have to do more, than put url-s to your db table or csv file, and waiting for the emails.
Upvotes: 0
Reputation: 1135
This function can do the task:
function mail_page_contents( $url = '' )
{
if( $url && ( $contents = file_get_contents( $url ) ) )
{
$emailto = '[email protected]';
$subject = 'Webpage Contents';
mail( $emailto , $subject , $contents );
}
}
Upvotes: 1