2014-11-11 2 views
2

Я не могу загрузить скрипт загрузки для работы с внешними файлами, файл будет загружен, но поврежден/не работает. Я думаю, это потому, что я не могу получить размер файла внешнего файла с функцией filesize().Загрузить внешний файл с PHP fopen и/или cURL

Это мой сценарий:

function getMimeType($filename){ 
    $ext = pathinfo($filename, PATHINFO_EXTENSION); 
    $ext = strtolower($ext); 

    $mime_types=array(
     "pdf" => "application/pdf", 
     "txt" => "text/plain", 
     "html" => "text/html", 
     "htm" => "text/html", 
     "exe" => "application/octet-stream", 
     "zip" => "application/zip", 
     "doc" => "application/msword", 
     "xls" => "application/vnd.ms-excel", 
     "ppt" => "application/vnd.ms-powerpoint", 
     "gif" => "image/gif", 
     "png" => "image/png", 
     "jpeg"=> "image/jpg", 
     "jpg" => "image/jpg", 
     "php" => "text/plain", 
     "csv" => "text/csv", 
     "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 
     "pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", 
     "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document" 
    ); 

    if(isset($mime_types[$ext])){ 
     return $mime_types[$ext]; 
    } else { 
     return 'application/octet-stream'; 
    } 
} 

$path = "http://www.example.com/file.zip"; 

/* Does not work on external files 
// check file is readable or not exists 
if (!is_readable($path)) 
    die('File is not readable or does not exists!'); 
*/ 

$file_headers = @get_headers($path); 
if($file_headers[0] == 'HTTP/1.1 404 Not Found') { 
    echo "Files does not exist."; 
} else { 

$filename = pathinfo($path, PATHINFO_BASENAME); 

// get mime type of file by extension 
$mime_type = getMimeType($filename); 

// set headers 
header('Pragma: public'); 
header('Expires: -1'); 
header('Cache-Control: public, must-revalidate, post-check=0, pre-check=0'); 
header('Content-Transfer-Encoding: binary'); 
header("Content-Disposition: attachment; filename=\"$filename\""); 
header("Content-Length: " . filesize($path)); 
header("Content-Type: $mime_type"); 
header("Content-Description: File Transfer"); 

// read file as chunk 
if ($fp = fopen($path, 'rb')) { 
    ob_end_clean(); 

    while(!feof($fp) and (connection_status()==0)) { 
     print(fread($fp, 8192)); 
     flush(); 
    } 

    @fclose($fp); 
    exit; 
} 

} 

Я считаю, что это можно сделать с помощью Curl - но мои знания не хватает. То, что я хотел бы знать:

  • Как проверить, если файл существует и как я могу получить размер файла с Curl?

  • Было бы лучше просто использовать cURL и забыть о fopen?

  • Правильно ли установлены заголовки?

Любые советы очень ценятся!

+0

Что происходит, когда вы опускаете заголовок Content-Length :. Можете ли вы загрузить весь файл перед его выпуском? – iewebguy

+0

Просьба указать некоторые результаты попытки отладки проблемы. –

+0

ОТВЕТ: ДА, забудьте о 'fopen' и используйте' curl'. – khael

ответ

0

Вы можете попробовать этот процесс, а также, я предполагаю, что ваш источник URL является $sourceUrl и назначения/путь для сохранения файла $destinationPath

$destFilename = 'my_file_name.ext'; 
$destinationPath = 'your/destination/path/'.$destFilename; 

if(ini_get('allow_url_fopen')) {         
    if(! @file_put_contents($destinationPath, file_get_contents($sourceUrl))){ 
     $http_status = $http_response_header[0]; 
     sprintf('%s encountered while attempting to download %s',$http_status, $sourceUrl); 
     break; 
    } 
} elseif(function_exists('curl_init')) { 
    $ch = curl_init($sourceUrl); 
    $fp = fopen($destinationPath, "wb"); 

    $options = array(
     CURLOPT_FILE => $fp, 
     CURLOPT_HEADER => 0, 
     CURLOPT_FOLLOWLOCATION => 1, 
     CURLOPT_TIMEOUT => 120); // in seconds 

    curl_setopt_array($ch, $options); 
    curl_exec($ch); 
    $http_status = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE)); 
    curl_close($ch); 
    fclose($fp); 

    //delete the file if the download was unsuccessful 
    if($http_status != 200) { 
     unlink($destinationPath); 
     sprintf('HTTP status %s encountered while attempting to download %s', $http_status, $sourceUrl); 

    } 
} else {  
    sprintf('Looks like %s is off and %s is not enabled. No images were imported.', '<code>allow_url_fopen</code>', '<code>cURL</code>' ); 
    break; 
} 

Вы можете использовать curl_getinfo($ch, CURLINFO_CONTENT_TYPE); в случае завивки, чтобы получить информацию о файле и использовать его согласно вашему требованию.

1

Проблема возникает из-за вашей длины контента, которая устанавливается в 0. Поскольку у вас уже есть контент-длина от get_headers вызова, просто измените следующую строку:

header("Content-Length: " . filesize($path)); 

к:

header($file_headers[8]); 

Обратите внимание, что содержание $file_headers может изменяться (8 работали для меня) , проверьте manual для получения дополнительной информации или выполните команду print_r($file_headers), чтобы увидеть, что вы там получаете.

Если вы не заботитесь о заголовке длины контента, просто прокомментируйте это, большинство браузеров должны справиться с этим без каких-либо проблем.

+0

Вы понимаете, что настройка заголовков исполняемых сценариев не оказывает большого влияния на запрос, который он пытается сделать с внешним URL-адресом через fopen. – khael

+0

Его код работает, нет ничего плохого в использовании fopen, его единственная проблема связана с тем, что он пропускает 0 в качестве заголовка длины содержимого. – Julien

+0

Но мой вопрос состоял в том, что заголовки установлены для того, что вернет скрипт php-скрипта (что означает использование 'header()'), и это влияет на 'запрос', который он делал с fopen на внешнем URL-адресе. Надеюсь, я это разъяснил. – khael

0

Функция:

<?php 
 
/** 
 
* Returns the size of a file without downloading it, or -1 if the file 
 
* size could not be determined. 
 
* 
 
* @param $url - The location of the remote file to download. Cannot 
 
* be null or empty. 
 
* 
 
* @return The size of the file referenced by $url, or -1 if the size 
 
* could not be determined. 
 
*/ 
 
function curl_get_file_size($url) { 
 
    // Assume failure. 
 
    $result = -1; 
 

 
    $curl = curl_init($url); 
 

 
    // Issue a HEAD request and follow any redirects. 
 
    curl_setopt($curl, CURLOPT_NOBODY, true); 
 
    curl_setopt($curl, CURLOPT_HEADER, true); 
 
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 
 
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); 
 
    curl_setopt($curl, CURLOPT_USERAGENT, get_user_agent_string()); 
 

 
    $data = curl_exec($curl); 
 
    curl_close($curl); 
 

 
    if($data) { 
 
    $content_length = "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)) { 
 
     $content_length = (int)$matches[1]; 
 
    } 
 

 
    // http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 
 
    if($status == 200 || ($status > 300 && $status <= 308)) { 
 
     $result = $content_length; 
 
    } 
 
    } 
 

 
    return $result; 
 
} 
 
?>

Вызов функции:

$file_size = curl_get_file_size("http://stackoverflow.com/questions/2602612/php-remote-file-size-without-downloading-file");

0

Попробуйте использовать что-то вроде этого:

function get_data($url) 
{ 
    $ch = curl_init(); 
    $timeout = 5; 
    curl_setopt($ch, CURLOPT_URL, $url); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); 
    $data = curl_exec($ch); 
    curl_close($ch); 

    return $data; 
} 

К сожалению, у вас не хватает деталей о вашем конкретном запросе или файлах, и мне не удалось найти более точный код в соответствии с вашей ситуацией. И выше (или ниже) curl_get_file_size поможет вам с размером в случае, если вам это понадобится.

1

этот код отлично работает для загрузки из URL:

set_time_limit(0); 

//File to save the contents to 
$fp = fopen ('r.jpg', 'w+'); 

$url = "http://cgr.ir/test.jpg"; 

//Here is the file we are downloading, replace spaces with %20 
$ch = curl_init(str_replace(" ","%20",$url)); 

curl_setopt($ch, CURLOPT_TIMEOUT, 50); 

//give curl the file pointer so that it can write to it 
curl_setopt($ch, CURLOPT_FILE, $fp); 
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 

$data = curl_exec($ch);//get curl response 

//done 
curl_close($ch); 
?> 
0

IMHO Это хорошая идея не полагаться на доступность модуля php curl. Ваш фрагмент кода работает с небольшой модификацией:

Первое изменение

$file_headers = @get_headers($path); 

в

$file_headers = @get_headers($path,1); 

получить именованные ключи массива (см php reference).

С этой модификацией код статуса http по-прежнему поставляется в $ file_headers [0], но вы получите еще несколько полезных и полезных данных, которые могут быть переданы (рекомендуется проверка): Content-Length и даже Content-Type (что позволяет вы отказываетесь от вашего подхода обнаружения типа mime при суффиксе файла).

Изменить

header("Content-Length: " . filesize($path)); 

в

header("Content-Length: " . $file_headers['Content-Length']); 

и

header("Content-Type: $mime_type"); 

в

header("Content-Type: " . $file_headers['Content-Type']); 

Даже если ваш «путь» является надежным источником, вам может потребоваться добавить некоторую проверку, так как вы не должны доверять внешним данным, которые вы ожидаете.