How do I keep an embedded browser from prompting where to save a downloaded file?

馋奶兔 提交于 2019-12-30 07:32:33

问题


How to down load a file after clicking a download button programatically, and therefore not needing to know the url for the downloading file.

After a file has downloaded a prompt comes up and asks if you'd like to save the file, after pressing 'yes' another prompt asks where you'd like to save the file. So, the file is downloaded first, maybe into a buffer somewhere, after the initial download, the prompts appear.

So, once the button is clicked how do you capture the downloading stream and save it as a file somewhere, without the popup prompts appearing?

(Any method for clicking a button would be fine, the following should be fine.)

procedure TForm1.Button1Click(Sender: TObject);
var
  x: integer;
  ovLinks: OleVariant;
begin
  WebBrowser1.Navigate('The web page');
  //wait for page to down load
  ovLinks := WebBrowser1.OleObject.Document.all.tags('A');
  if ovLinks.Length > 0 then
  begin
    for x := 0 to ovLinks.Length-1 do
      begin
        if Pos('id of button', ovLinks.Item(x).id) > 0 then
        //or if Pos('href of button', ovLinks.Item(x).href) > 0 then
        begin
          ovLinks.Item(x).click;
          Break;
        end;
      end;
  end;
end;

The reason for this question is: the url of a file can not always be found. Eg: At this web site, I couldn't find the url programatically but after pressing the export button, using IE, the file was download into the 'Temporary Internet Files' folder. In the IE 'Temporary Internet Files' folder it has a column 'Internet adress' which shows the url. But in Chrome no such data exists. BUT, at this web site, I can find the url programatically, but when I download the file, by pressing 'here', the file doesn't appear in the IE 'Temporary Internet Files' folder. For other websites, the url can be found in the folder and by finding it programatically, but at other sites the url can not be found either way.


回答1:


Implement the IDownloadManager interface with its Download method to your web browser control and you can simply control what you need. The Download method is called whenever you're going to download a file (only when the save as dialog pops up).

1. Embedded Web Browser

You can use the Embedded Web Browser control which has this interface already implemented and which fires the OnFileDownload that is different from the same named event in TWebBrowser. See for instance this thread on how to use it.

2. Do it yourself

Another option is that you can implement it to TWebBrowser by yourself. In the following example I've used interposed class just for showing the principle, but it's very easy to wrap it as a component (that's why I've made the OnBeforeFileDownload published).

2.1. OnBeforeFileDownload event

The only extension to TWebBrowser in this interposed class is the OnBeforeFileDownload event which fires when the file is going to be downloaded (before save as dialog pops up, but instead of the OnFileDownload event, not when the document itself is downloaded). If you won't write the event handler for it, the web browser control will behave as before (showing a save as dialog). If you write the event handler and return False to its Allowed declared parameter, the file saving will be cancelled. If you return True to the Allowed parameter (what is by default), the save as dialog will be shown. Note that if you cancel downloading by setting Allowed to False, you'll need to download the file by yourself (as I did synchronously using Indy in this example). For this purpose there's the FileSource constant parameter, which contains the downloaded file URL. Here is the event parameters overview:

  • Sender (TObject) - event sender
  • FileSource (WideString) - source file URL
  • Allowed (Boolean) - declared boolean parameter, which decides if the file download will be allowed or not (default value is True)

2.2. IDownloadManager implementation

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  StdCtrls, OleServer, OleCtrls, Dialogs, ActiveX, MSHTML, UrlMon, SHDocVw,
  IdHTTP;

const
  IID_IDownloadManager: TGUID = '{988934A4-064B-11D3-BB80-00104B35E7F9}';
  SID_SDownloadManager: TGUID = '{988934A4-064B-11D3-BB80-00104B35E7F9}';

type
  IDownloadManager = interface(IUnknown)
    ['{988934A4-064B-11D3-BB80-00104B35E7F9}']
    function Download(pmk: IMoniker; pbc: IBindCtx; dwBindVerb: DWORD;
      grfBINDF: DWORD; pBindInfo: PBindInfo; pszHeaders: PWideChar;
      pszRedir: PWideChar; uiCP: UINT): HRESULT; stdcall;
  end;
  TBeforeFileDownloadEvent = procedure(Sender: TObject; const FileSource: WideString;
    var Allowed: Boolean) of object;
  TWebBrowser = class(SHDocVw.TWebBrowser, IServiceProvider, IDownloadManager)
  private
    FFileSource: WideString;
    FOnBeforeFileDownload: TBeforeFileDownloadEvent;
    function QueryService(const rsid, iid: TGUID; out Obj): HRESULT; stdcall;
    function Download(pmk: IMoniker; pbc: IBindCtx; dwBindVerb: DWORD;
      grfBINDF: DWORD; pBindInfo: PBindInfo; pszHeaders: PWideChar;
      pszRedir: PWideChar; uiCP: UINT): HRESULT; stdcall;
  protected
    procedure InvokeEvent(ADispID: TDispID; var AParams: TDispParams); override;
  published
    property OnBeforeFileDownload: TBeforeFileDownloadEvent read FOnBeforeFileDownload write FOnBeforeFileDownload;
  end;

type
  TForm1 = class(TForm)
    Button1: TButton;
    WebBrowser1: TWebBrowser;
    FileSourceLabel: TLabel;
    FileSourceEdit: TEdit;
    ShowDialogCheckBox: TCheckBox;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    procedure BeforeFileDownload(Sender: TObject; const FileSource: WideString;
      var Allowed: Boolean);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TWebBrowser }

function TWebBrowser.Download(pmk: IMoniker; pbc: IBindCtx; dwBindVerb,
  grfBINDF: DWORD; pBindInfo: PBindInfo; pszHeaders, pszRedir: PWideChar;
  uiCP: UINT): HRESULT;
var
  Allowed: Boolean;
begin
  Result := E_NOTIMPL;
  if Assigned(FOnBeforeFileDownload) then
  begin
    Allowed := True;
    if pszRedir <> '' then
      FFileSource := pszRedir;
    FOnBeforeFileDownload(Self, FFileSource, Allowed);
    if not Allowed then
      Result := S_OK;
  end;
end;

procedure TWebBrowser.InvokeEvent(ADispID: TDispID; var AParams: TDispParams);
begin
  inherited;
  // DispID 250 is the BeforeNavigate2 dispinterface and to the FFileSource here
  // is stored the URL parameter (for cases, when the IDownloadManager::Download
  // won't redirect the URL and pass empty string to the pszRedir)
  if ADispID = 250 then
    FFileSource := OleVariant(AParams.rgvarg^[5]);
end;

function TWebBrowser.QueryService(const rsid, iid: TGUID; out Obj): HRESULT;
begin
  Result := E_NOINTERFACE;
  Pointer(Obj) := nil;
  if Assigned(FOnBeforeFileDownload) and IsEqualCLSID(rsid, SID_SDownloadManager) and
    IsEqualIID(iid, IID_IDownloadManager) then
  begin
    if Succeeded(QueryInterface(IID_IDownloadManager, Obj)) and
      Assigned(Pointer(Obj))
    then
      Result := S_OK;
  end;
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  HTMLWindow: IHTMLWindow2;
  HTMLDocument: IHTMLDocument2;
begin
  WebBrowser1.Navigate('http://financials.morningstar.com/income-statement/is.html?t=AAPL&ops=clear');
  while WebBrowser1.ReadyState <> READYSTATE_COMPLETE do
    Application.ProcessMessages;

  HTMLDocument := WebBrowser1.Document as IHTMLDocument2;
  if not Assigned(HTMLDocument) then
    Exit;
  HTMLWindow := HTMLDocument.parentWindow;
  if Assigned(HTMLWindow) then
  try
    HTMLWindow.execScript('SRT_stocFund.Export()', 'JavaScript');
  except
    on E: Exception do
      ShowMessage(E.Message);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown := True;
  WebBrowser1.OnBeforeFileDownload := BeforeFileDownload;
end;

procedure TForm1.BeforeFileDownload(Sender: TObject; const FileSource: WideString;
  var Allowed: Boolean);
var
  IdHTTP: TIdHTTP;
  FileTarget: string;
  FileStream: TMemoryStream;
begin
  FileSourceEdit.Text := FileSource;
  Allowed := ShowDialogCheckBox.Checked;
  if not Allowed then
  try
    IdHTTP := TIdHTTP.Create(nil);
    try
      FileStream := TMemoryStream.Create;
      try
        IdHTTP.HandleRedirects := True;
        IdHTTP.Get(FileSource, FileStream);
        FileTarget := IdHTTP.URL.Document;
        if FileTarget = '' then
          FileTarget := 'File';
        FileTarget := ExtractFilePath(ParamStr(0)) + FileTarget;
        FileStream.SaveToFile(FileTarget);
      finally
        FileStream.Free;
      end;
    finally
      IdHTTP.Free;
    end;
    ShowMessage('Downloading finished! File has been saved as:' + sLineBreak +
      FileTarget);
  except
    on E: Exception do
      ShowMessage(E.Message);
  end;
end;

end.

2.3. IDownloadManager project

You can download the above code (written in Delphi 2009) as a complete project from here.




回答2:


I don't know if this will get you where you need to go, but it seems promising. With the TWebBrowser I have here (exported from "Microsoft Internet Controls version 1.1"), you can use the OnBeforeNavigate2 event to monitor all the URLs the web browser handles. The problem you have from there would be to determine what you need to do, capture the URL, and then handle it yourself. Here's a short example from the five minutes I was playing with the control on the first web site you presented.

procedure TForm1.WebBrowser1BeforeNavigate2(Sender: TObject;
     pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
     Headers: OleVariant; var Cancel: WordBool);
  begin
    Edit1.Text := String(URL);
    if Pos('CSV', Edit1.Text) > 0 then
      Cancel := true;
  end;

As you can see, there's a lot of parms and you'd have to locate the documentation to see what those mean. But in my short example, what I do is put the navigated URLs to the Edit1.Text (probably better a TMemo if you really want to watch what is going on). Given your example, there's really nothing to indicate it's a directly downloaded file, but using the code above, I can cancel the browser from doing it's thing (show the download prompts, etc), and then have the URL there in the Edit1 box to act upon. If one were to dig further, I'm sure you can look at the headers in question and determine if the web site intends to send you a file that you should be downloading, since the URL in and of itself doesn't say "CSV file" (putting http://financials.morningstar.com/ajax/ReportProcess4CSV.html?t=AAPL&region=usa&culture=us_EN&reportType=is&period=12&dataType=A&order=asc&columnYear=5&rounding=3&view=raw&productCode=USA&r=809199&denominatorView=raw&number=3 into a web browser will download the CSV file in question).

Hopefully it's a good start for you.



来源:https://stackoverflow.com/questions/13377779/how-do-i-keep-an-embedded-browser-from-prompting-where-to-save-a-downloaded-file

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!