7

如果文件超过 5mb,如何阻止远程文件下载文件?如果我在传输时停止它,文件是否会保存在其他临时目录或内存中?我怎么会知道?这是我当前的代码:

$url = 'http://www.spacetelescope.org/static/archives/images/large/heic0601a.jpg';
$file = '../temp/test.jpg';
file_put_contents($file, file_get_contents($url));
4

4 回答 4

8

有很多方法可以做到这一点,但因为你目前正在使用file_get_contents(),所以我会这样做:

  • 使用打开远程文件fopen()
  • 读取文件并使用增量保存它fread()- 这允许您跟踪当前大小并在超过阈值时抛出和错误。

像这样的东西:

$url = 'http://www.spacetelescope.org/static/archives/images/large/heic0601a.jpg';
$file = '../temp/test.jpg';
$limit = 5 * 1024 * 1024; // 5MB

if (!$rfp = fopen($url, 'r')) {
  // error, could not open remote file
}
if (!$lfp = fopen($file, 'w')) {
  // error, could not open local file
}

// Check the content-length for exceeding the limit
foreach ($http_response_header as $header) {
  if (preg_match('/^\s*content-length\s*:\s*(\d+)\s*$/', $header, $matches)) {
    if ($matches[1] > $limit) {
      // error, file too large
    }
  }
}

$downloaded = 0;

while (!feof($rfp) && $downloaded < $limit) {
  $chunk = fread($rfp, 8192);
  fwrite($lfp, $chunk);
  $downloaded += strlen($chunk);
}

if ($downloaded > $limit) {
  // error, file too large
  unlink($file); // delete local data
} else {
  // success
}

注意:在您收到任何文件之前检查文件头以检查文件是否太大是很有诱惑力的Content-Length:- 如果您愿意,您仍然可以这样做,但不要相信值!标头本质上是一个任意值,虽然使用与文件大小不匹配的内容长度会违反协议,但它可以用来欺骗系统下载违反规则的文件。即使您确实检查了此值,您也需要计算字节数。

您也可以通过数据回调使用 curl 来执行此操作,但我认为这是一个不太令人满意的解决方案,主要是因为 curl 需要字符串函数名称而不是标准泛型callable类型,这意味着您要么需要使用全局变量或static变量以跟踪下载的内容长度,这两者都不可接受(恕我直言)。

于 2012-12-20T00:04:35.693 回答
2

至于获取 URL 并限制大小,这并不太难……但file_get_contents不会这样做。您可以fopen使用 url,并stream_get_meta_data在流上使用来获取有关流的信息数组。如果我正在正确阅读示例(并且如果传输编码不是“分块”),您会Content-Length在其中找到标题,它将告诉您文件大小。如果该值已设置并且超过您的限制,则保释。

如果流元数据没有该信息,或者信息表明文件足够小,则您仍然拥有流本身;您仍然可以fread从远程文件fwrite到本地文件,直到您全部阅读或达到限制。当然,如果你达到了这一点,一旦你达到限制,文件仍然存在;你必须自己删除它。

于 2012-12-20T00:06:01.050 回答
1

这是不可能的file_get_contents- 您需要使用 PHP cURL 扩展来获取文件大小。

<?php
$max_length = 5 * 1024 * 1024;
$url = 'http://www.spacetelescope.org/static/archives/images/large/heic0601a.jpg';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$data = curl_exec($ch);
curl_close($ch);
if ($data === false) {
  echo 'cURL failed';
  exit;
}
$contentLength = 'unknown';
if (preg_match('/Content-Length: (\d+)/', $data, $matches)) {
  $contentLength = (int)$matches[1];
}
echo "Content-Length: $contentLength\n";

if ($contentLength > $max_length) {
    echo "File is too large\n";
} else {
    echo "File size is ok\n";
}

将其包装在一个函数中并使用它来检查 URL - 任何太大的东西都可以跳过。

缺点:

此方法仅Content-Length在服务器返回标头时有效 - 并非所有服务器都表现良好,并且并非所有服务器都会返回正确的长度。

优势:

无需下载和写入$max_length字节。此方法将更快且资源消耗更少。

修改自:

http://php.net/filesize#92462

于 2012-12-20T00:06:05.943 回答
1

这是 DaveRandom 解决方案的一个变体,但在完全加载之前不写入文件。

<?php
define('MAXMB', 5);
define('BYTESPERMB', 1048576);

$maxsize = MAXMB * BYTESPERMB;

$handle = fopen("http://www.example.com/", "rb");
$contents = '';
$completed = true;
while (!feof($handle)) {
  $chunk = fread($handle, 8192);

  $size += strlen($chunk);
  if ($size > $maxsize) {
      $completed = false;
      break;
  }
  $contents .= $chunk;
}
fclose($handle);

if ($completed) {
  // File appears to be completely downloaded so write it --
  file_put_contents('path/to/localfile', $contents);

} else {
  // It was too big
}
于 2012-12-20T00:08:54.960 回答