Rechalow 2022-05-28 18:05 采纳率: 60%
浏览 47
已结题

Delphi无法正确获取网络文件大小

问题遇到的现象和发生背景

我在使用Delphi11.1的时候,想使用TIdHttp组件来达到从网络中用多线程下载的目的。【这里是Delphi!!】
现在有个很严重的问题!我无法获取网络中的文件大小。

我试过,如果用小文件下载是非常可以的!比如说我从网上下载一个100KB的文件,就非常可以下载。但是当我从python官网中下载一个install安装包的时候,这个安装包有27多MB,然后就会报错,报错位置就是出在获取文件大小这一部分。
我的代码是这里的。http://www.360doc.com/content/10/0818/13/2071424_46919572.shtml
然后,就没办法获取文件大小。出的错误在这里:

问题相关代码,请勿粘贴截图

我的代码都是看了这上面的帖子写的!https://wenku.baidu.com/view/14617e36e%F0%9F%90%8Eefdc8d376e%F0%9F%90%8Ee3242.html

unit UnzipMethod;

interface

uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, ComCtrls, StdCtrls, IdComponent, IdTCPConnection, IdTCPClient,
    IdHTTP, IdBaseComponent, IdAntiFreezeBase, IdAntiFreeze,
    IdThreadComponent, IdFTP;

type
    TThread1 = class(TThread)
  private
      fCount, tstart, tlast: integer;
      tURL, tFile, temFileName: string;
      tResume: Boolean;
      tStream: TFileStream;
  protected
      procedure Execute; override;
  public
      constructor create1(aURL, aFile, fileName: string; bResume: Boolean; Count,start, last: integer);
      procedure DownLoadFile(); //下载文件
  end;
type
    TForm1 = class(TForm)
        IdAntiFreeze1: TIdAntiFreeze;
        IdHTTP1: TIdHTTP;
        Button1: TButton;
        ProgressBar1: TProgressBar;
        IdThreadComponent1: TIdThreadComponent;
        Label1: TLabel;
        Label2: TLabel;
        ListBox1: TListBox;
        Edit1: TEdit;
        Edit2: TEdit;
        Label3: TLabel;
        Label4: TLabel;
    Button2: TButton;
    Button3: TButton;
        procedure Button1Click(Sender: TObject);
        procedure IdHttp1WorkBegin(Sender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
        procedure IdHttp1Work(Sender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
        procedure Button2Click(Sender: TObject);
        procedure IdHTTP1Status(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String);
        procedure Button3Click(Sender: TObject);
  private
    public
        nn, aFileSize, avg: Integer;
        MyThread: array[1..256] of TThread;
        procedure GetThread();
        procedure AddFile();
        function GetURLFileName(aURL: String): String;
        function GetFileSize(aURL: String): Integer;
    end;
var
    Form1: TForm1;

implementation

var
    AbortTransfer: Boolean;
    aURL, aFile: String;
    tcount: Integer;

{$R *.dfm}

//接收文件
function TForm1.GetURLFileName(aURL: string): string;
var
    i: integer;
    s: string;
begin //返回下载地址的文件名
    s := aURL;
    i := Pos('/', s);
    while i <> 0 do //去掉"/"前面的内容剩下的就是文件名了
    begin
        Delete(s, 1, i);
        i := Pos('/', s);
    end;
    Result := s;
end;

//得到文件大小
function TForm1.GetFileSize(aURL: string): integer;//**********************错误就发生在这里********************
var
    FileSize: integer;
begin
    IdHTTP1.Head(aURL);
    FileSize := IdHTTP1.Response.ContentLength;
    IdHttp1.Disconnect;
    Result := FileSize;
  //messagebox(Handle, pchar(inttostr(FileSize)), '', 0);
end;
//多线程下载
procedure TForm1.Button1Click(Sender: TObject);
var
    j: integer;
begin
  Form1.ListBox1.Clear;
  Form1.ProgressBar1.Position := 0;
    tcount := 0;
    Showmessage('OK!主线程在执行,获得文件名并显示在Edit5中');
    aURL := Edit1.Text; //下载地址
    aFile := GetURLFileName(Edit1.Text); //得到文件名
    nn := StrToInt(Edit2.Text); //线程数
    j := 1;
    aFileSize := GetFileSize(aURL);
    avg := trunc(aFileSize / nn);
    try
        GetThread();
        while j <= nn do
        begin
            MyThread[j].Resume; //唤醒线程
            j := j + 1;
        end;
    except
        Showmessage('创建线程失败!');
        Exit;
    end;
end;

//开始下载前,将ProgressBar1的最大值设置为需要接收的数据大小.*******************
procedure TForm1.IdHttp1WorkBegin(Sender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
begin
    AbortTransfer := False;
    ProgressBar1.Max := AWorkCountMax;
    ProgressBar1.Min := 0;
    ProgressBar1.Position := 0;
end;
//接收数据的时候,进度将在ProgressBar1显示出来。
procedure TForm1.IdHttp1Work(Sender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
begin
    if AbortTransfer then
    begin
        IdHttp1.Disconnect;//中断下载
    end;
    ProgressBar1.Position := AWorkCount;
    //ProgressBar1.Position := ProgressBar1.Position + AWorkCount;//*******显示速度极快
    Application.ProcessMessages;//这样使用不知道对不对
end;

//中断下载
procedure TForm1.Button2Click(Sender: TObject);
begin
    AbortTransfer := false;
    IdHttp1.Disconnect;
end;
//状态显示
procedure TForm1.IdHTTP1Status(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String);
begin
    ListBox1.ItemIndex := ListBox1.Items.Add(AStatusText);
end;
//退出程序
procedure TForm1.Button3Click(Sender: TObject);
begin
    Application.Terminate;
end;
//循环产生线程
procedure TForm1.GetThread();
var
    i: integer;
    start, last: array[1..256] of Integer;
    FileName: String;
begin
    i := 1;
    while i <= nn do
    begin
        start[i] := avg * (i - 1);
        last[i] := avg * i - 1;//这里原先时last := avg * i
        if i = nn then
        begin
            last[i] := avg * i + aFileSize - avg * nn;//这里原先时aFileSize
        end;
        fileName := aFile + inttostr(i);
        MyThread[i] := TThread1.create1(aURL, aFile, fileName, false, i, start[i], last[i]);
        i := i + 1;
    end;
end;
procedure TForm1.AddFile();//合并文件
var
    mStream1, mStream2: TMemoryStream;
    i: Integer;
begin
    i := 1;
    mStream1 := TMemoryStream.Create;
    mStream2 := TMemoryStream.Create;
    mStream1.LoadFromFile(aFile + '1');
    while i < nn do
    begin
        mStream2.LoadFromFile(aFile + inttostr(i + 1));
        mStream1.Seek(mStream1.size, soFromBeginning);
        mStream1.CopyFrom(mStream2, mStream2.size);
        mStream2.Clear;
        i := i + 1;
    end;
    mStream2.Free;
    mStream1.SaveToFile(aFile);
    mStream1.Free;
    //删除临时文件
    i := 1;
    while i <= nn do
    begin
        deleteFile(aFile + inttostr(i));
        i := i + 1;
    end;
    Form1.ListBox1.ItemIndex := Form1.ListBox1.Items.Add('下载成功');
end;
//构造函数
constructor TThread1.create1(aURL, aFile, FileName: String; bResume: Boolean; Count, start, last: integer);
begin
    inherited create(true);
    FreeOnTerminate := true;
    tURL := aURL;
    tFile := aFile;
    fCount := Count;
    tResume := bResume;
    tstart := start;
    tlast := last;
    temFileName:= FileName;
end;

//下载文件函数
procedure TThread1.DownLoadFile();
var
    temHttp: TIdHTTP;
begin
    temHttp := TIdHTTP.Create(nil);
    temHttp.OnWorkBegin := Form1.IdHttp1WorkBegin;
    temHttp.OnWork := Form1.IdHttp1Work;
    temHttp.OnStatus := Form1.IdHttp1Status;
    Form1.IdAntiFreeze1.OnlyWhenIdle := False;//设置使程序有反应
    if FileExists(temFileName) then //如果文件已经存在
        tStream := TFileStream.Create(temFileName, fmOpenWrite)
    else
        tStream := TFileStream.Create(temFileName, fmCreate);
    if tResume then //续传方式
    begin
        exit;
    end
    else //覆盖或新建方式
    begin
        temHttp.Request.ContentRangeStart := tstart;
        temHttp.Request.ContentRangeEnd := tlast;
    end;
    try //开始下载
        //TIdFTP1.Get(temFileName,tStream,true); //开始下载
        temHttp.Get(tURL,tStream); //开始下载
        Form1.ListBox1.ItemIndex := Form1.ListBox1.Items.Add(temFileName + 'download');
    finally
        FreeAndNil(tStream);
        temHttp.Disconnect;
    end;
end;

procedure TThread1.Execute;
begin
    if Form1.Edit1.Text <> '' then
        DownLoadFile
    else
        exit;
    inc(tcount);
    if tcount = Form1.nn then
    begin
        Form1.ListBox1.ItemIndex := Form1.ListBox1.Items.Add('正在合并下载文件!');
        Form1.AddFile;
    end;
end;

end.
运行结果及报错内容

第一次在$770FED42遇到异常.异常类 EIdOSSLUnderlyingCryptoError,消息
'Error connecting with SSL.
error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version'.
进程Test.exe (3844)

我的解答思路和尝试过的方法

尝试过使用FTP下载,但也没有用……FTP始终是用不习惯。很多方法都没有

我想要达到的结果

能够使文件部分正确获取到文件大小。哪怕遇到稍大的文件也可以。

  • 写回答

1条回答 默认 最新

  • Rechalow 2022-05-28 21:20
    关注

    好了,这个问题已经在QQ群里问了解决了,说是协议问题。好了,没事了,就这样吧!
    新建一个组件名为【TIdSSLIOHandlerSocketOpenSSL】,在里面把属性【SSLOptions\Method】调成【sslvTLSv1_2】,然后把【TIdHttp】组件的属性【IOHandle】调成这个【SSLIOHandler】组件就可以了。
    当然,以上组件的话,也可以直接在代码里面调整。

    var
    ssl: TIdSSLIOHandlerSocketOpenSSL;
    http: TIdHttp;
    begin
    ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
    http := TIdHttp.Create(nil);
    ssl.SSLOptions.Method := TIdSSLVersion.sslvTLSv1_2; //这里有个固定的方法为TIdSSLVersion,可以用这个修改协议。
    http.IOHandle := ssl; //这里把TIdHttp的IOHandle属性调成ssl即可!!
    //
    // TODO 写接下来需要用到Http的事情。。。
    //
    end;
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 系统已结题 6月5日
  • 已采纳回答 5月28日
  • 修改了问题 5月28日
  • 修改了问题 5月28日
  • 展开全部

悬赏问题

  • ¥15 请问paddlehub能支持移动端开发吗?在Android studio上该如何部署?
  • ¥170 如图所示配置eNSP
  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效
  • ¥15 悬赏!微信开发者工具报错,求帮改
  • ¥20 wireshark抓不到vlan
  • ¥20 关于#stm32#的问题:需要指导自动酸碱滴定仪的原理图程序代码及仿真
  • ¥20 设计一款异域新娘的视频相亲软件需要哪些技术支持
  • ¥15 stata安慰剂检验作图但是真实值不出现在图上
  • ¥15 c程序不知道为什么得不到结果