PHP:给作为附件的电子邮件提供URL内容(不下载)?

时间:2013-04-05 15:38:56

标签: php curl sendmail email-attachments

底部的新问题

我想编写一个PHP脚本,可以发送附加到电子邮件的给定URL的内容,而无需下载和上传。

我的资源有限:正常托管帐户 PHP 64M内存限制 ~1GB存储空间

我的第一个代码是带有fopen()的简单sendmail,但如果给定的文件太大,则会出现内存问题。我该如何解决这个问题?

<?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="no-reply@domain.com"; // 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);
}
?>

这种方法的问题:

  

73,214,655字节   致命错误:第58行/url2mail.php中允许的内存大小为67108864字节(尝试分配66846720字节)   第58行是:$file = file_get_contents($file_url);

UPDATE 下面粘贴的工作版本,但仍受限于最大允许内存。我喜欢在内存中创建 Base 64编码片,而不是将文件下载到服务器。

<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>

所以,我的问题是:如何将给定文件(按URL)拆分为内存限制允许的切片?

例如

32M 内存限制允许我在内存中存储 ~30M ,因此我应该将 30M 文件拆分为3部分。

2 个答案:

答案 0 :(得分:1)

此功能可以完成任务:

function mail_page_contents( $url = '' )
{
    if( $url && ( $contents = file_get_contents( $url ) ) )
    {
        $emailto    = 'receiver@email.com';

        $subject    = 'Webpage Contents';

        mail( $emailto , $subject , $contents );
    }
}

答案 1 :(得分:0)

我会做这些步骤:

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. 

如果您像这样创建脚本,则不必执行更多操作,而是将url-s放入db表或csv文件,然后等待电子邮件。