You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C++Builder的TIdHTTP通过代理下载文件时遇EIdHTTPProtocolException异常

问题描述

使用TIdHTTP通过PHP代理下载文件时抛出EIdHTTPProtocolException异常,错误消息为待下载XML文件的第一行内容:<?xml version="1.0" encoding="UTF-8" standalone="yes"?>。直接通过浏览器访问目标URL(无论是否走代理)均可正常下载,但客户端代码走代理时触发报错。


客户端C++Builder代码

TIdHTTP *IdHTTP1 = new TIdHTTP(this); TFileStream *FileStream1;
try {
    FileStream1 = new TFileStream(myTempFileOnDisk, fmCreate);
    TIdSSLIOHandlerSocketOpenSSL *LHandler = new TIdSSLIOHandlerSocketOpenSSL(this);
    IdHTTP1->IOHandler = LHandler;
    IdHTTP1->HandleRedirects = true;

    IdHTTP1->Request->BasicAuthentication = true;
    IdHTTP1->Request->Username = "myUserName";
    IdHTTP1->Request->Password = "myBase64EncodedPassword";

    IdHTTP1->Request->UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0";
    //IdHTTP1->Get("https://www.myurl.com/myFileFolder/myFileToDownload.xml", FileStream1); // => 可正常运行!
    IdHTTP1->Request->ContentType = "text/xml; charset=\"UTF-8\"";
    IdHTTP1->Get("https://www.myurl.com/myProxy.php", FileStream1); // => 报错

    myBoxMessage = "Success!";
    if(IdHTTP1->ResponseCode != 200) myBoxMessage = "Failure : "+ IdHTTP1->ResponseText +".";
    LHandler->Free();
}
catch(EIdHTTPProtocolException &EX0Vl) {myLogMessage = EX0Vl.Message; myLogFunction();}
catch(EIdIOHandlerPropInvalid &EX0Vl) {myLogMessage = EX0Vl.Message; myLogFunction();}
catch(...) {myLogMessage = "Exception non planifiée."; myLogFunction();}

FileStream1->Free();

初始服务端PHP代理代码

if ($_SERVER['PHP_AUTH_PW'] == "myBase64EncodedPassword")
{
    try {
        $filename = (true)?"myFileToDownload.xml":"myFileToDownload.xlsx";
        $filepath = 'https://www.myurl.com/myFileFolder/'. $filename;
        if(fopen($filepath, 'r'))
        {
            header('Pragma: public');
            //header('Content-type: application/zip');
            header('Content-type: text/xml');
            //header('Content-Transfer-Encoding: Binary');
            header('Content-Transfer-Encoding: UTF-8');
            header('Content-Description: File Transfer');
            header('Content-Length: '. filesize($filename));
            header('Content-Disposition: attachment; filename='. $filename);
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header('Location: '. $filepath);
            header('Expires: 0'); 
            ob_clean(); flush();
            readfile($filepath);
            exit;
        } 
        else 
        {
            header('HTTP/1.1 404 Not found');
            error_log(date('Y-m-d H:i:s').", myProxy : File not found".PHP_EOL, 3, './myLogFile.log');   
            exit("File not found");
        }
    } 
    catch (Exception $EX0) 
    {
        header('HTTP/1.1 500 Internal Server Error');
        error_log(date('Y-m-d H:i:s').", myProxy : Internal Server Error".PHP_EOL, 3, './myLogFile.log');
        exit("Internal Server Error");
    }
}   

待下载XML文件内容

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ArticleTable xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
</ArticleTable>

更新后的服务端PHP代码(仍报错)

if ($_SERVER['PHP_AUTH_PW'] == "myBase64EncodedPassword")
{
    try {
        $filename = (true)?"myFileToDownload.xml":"myFileToDownload.xlsx";
        $filepath = 'https://www.myurl.com/myFileFolder/'. $filename;
        if(fopen($filepath, 'r'))
        {
            header('Pragma: public');
            //header('Content-type: application/zip');
            header('Content-type: text/xml; charset=UTF-8');
            header('Content-Transfer-Encoding: Binary');
            //header('Content-Transfer-Encoding: UTF-8');
            header('Content-Description: File Transfer');
            header('Content-Length: '. filesize($filename));
            header('Content-Disposition: attachment; filename='. $filename);
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header('Location: '. $filepath);
            header('Expires: 0'); 
            ob_clean(); flush();
            readfile($filepath);
            header('HTTP/1.1 200');
            exit(0);
        } 
        else 
        {
            header('HTTP/1.1 404 Not found');
            error_log(date('Y-m-d H:i:s').", myProxy : File not found".PHP_EOL, 3, './myLogFile.log');   
            exit("File not found");
        }
    } 
    catch (Exception $EX0) 
    {
        header('HTTP/1.1 500 Internal Server Error');
        error_log(date('Y-m-d H:i:s').", myProxy : Internal Server Error".PHP_EOL, 3, './myLogFile.log');
        exit("Internal Server Error");
    }
} 

解决思路
  1. 移除冲突的Location响应头:PHP代理中同时发送了Location跳转指令和readfile文件内容输出,导致HTTP响应格式混乱。浏览器会优先处理跳转,但TIdHTTP解析时会把多余的实体内容判定为错误信息。直接删除header('Location: '. $filepath);这一行,让代理直接输出文件内容。

  2. 修正Content-Length计算错误:filesize($filename)只能获取服务器本地文件大小,对远程HTTPS URL无效,会返回错误值,导致TIdHTTP校验响应长度失败。要么删除Content-Length头,让PHP自动处理;要么改用CURL先获取远程文件的正确大小:

    $ch = curl_init($filepath);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_exec($ch);
    $fileSize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
    curl_close($ch);
    header('Content-Length: '. $fileSize);
    
  3. 删除客户端冗余的ContentType设置:GET请求不需要设置Request->ContentType,这是用于指定POST/PUT请求的发送内容类型,直接删掉IdHTTP1->Request->ContentType = "text/xml; charset=\"UTF-8\"";。

  4. 调整HTTP状态码的发送顺序:更新后的PHP代码中header('HTTP/1.1 200');放在readfile之后,此时响应头已经发送,设置无效。默认响应状态码就是200,直接删除这一行即可。

  5. 替换fopen为更可靠的远程文件校验方式:fopen远程URL依赖allow_url_fopen配置,改用CURL校验文件是否存在更稳定:

    $ch = curl_init($filepath);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    $exists = curl_exec($ch);
    $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($exists && $statusCode == 200) {
        // 输出文件逻辑
    }
    

内容的提问来源于stack exchange,提问作者ibouka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 13:27:05