【问题标题】:IdHTTP or TFileStream file lock?IdHTTP 或 TFileStream 文件锁定?
【发布时间】:2020-05-19 15:09:06
【问题描述】:

我的应用程序向用户展示了一份论文列表(pdf 文件)。他们可以点击下载(通过TidHTTP)并在TWebBrowser 中显示pdf。如果文件已经存在,则跳过下载。上次我玩这个项目(2019 年秋季)时,这段代码还在工作,但现在当我在 iPhone 上运行它时,我遇到了问题。

症状:点击的第一篇论文会下载,然后在TWebBrowser中显示正常。任何后续点击的论文都会下载(我可以知道,因为我可以在我的应用程序文档文件夹中列出 *.pdf 文件)但无法显示。我捕获了当我将TWebBrowser 指向带有Form1->WebBrowser1->URL = "file://" + LFileName; 的文件时发生的错误。错误是“找不到指定的文件”。它在那里是因为我可以在上面列出目录。

如果我终止应用程序并重新启动它,然后返回并单击先前单击的文件之一(未显示),它会正常打开并显示在 TWebBrowser 中。这真的让我觉得这是某种文件锁定问题,因为文件存在。

代码如下:

void showPaper()
{
   // paperName (e.g. 22.pdf)

   UnicodeString LFileName = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetDocumentsPath(), paperNAME);
   if (!FileExists(LFileName)) { // file is not present so download it  
    UnicodeString URL = pdfURLv4 + paperNAME;  
    TFileStream* fs = new TFileStream(LFileName, fmCreate);
    Form1->Download->ConnectTimeout = 15000;  // give it 15 seconds
    Form1->Download->ReadTimeout = 15000;
    Form1->Download->Request->BasicAuthentication = true;
    Form1->Download->Request->Username = "XXXXXX";
    Form1->Download->Request->Password = "YYYYYY";
    Form1->Download->Request->UserAgent = "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0";
    try
    {
        Form1->Download->Get(URL, fs);
        Form1->Download->Disconnect();  // make sure socket is closed
    }
    catch(const System::Sysutils::Exception &)
    {
        try
        {
         UnicodeString URL = pdfURLv6 + paperNAME;  // the v6 url has brackets [] around host
         Form1->Download->Get(URL, fs);
         Form1->Download->Disconnect();  
        }
        catch(const System::Sysutils::Exception &)
        {
            ShowMessage(L"No/poor internet connection.");
            Form1->Download->Disconnect();  
            delete fs;
            return;
        }
    }
    delete fs;
   } // end of download if block


  if (FileExists(LFileName))    // have the file so open it
   {
   try 
   {
    Form1->WebBrowser1->URL = "file://" + LFileName;
   }
   catch ( const Exception& e )
   {
    ShowMessage(e.Message);
   }
    ShowMessage(Form1->WebBrowser1->URL);
  }
} // end of showPaper()

发生错误时,捕获的消息是(在运行 13.3 的 iPhone 上):

显示Form1->TWebBrowser1->URL 的 ShowMessage 给出了正确的答案:

我没有正确关闭TFileStream 吗?我可以杀死应用程序,重新启动并查看文件的事实让我知道文件正在正确下载。另外,第一次通过代码完全可以工作(下载然后显示在TWebBrowser)。只有在显示之前需要下载的后续尝试才会出现此“找不到文件”问题。

编辑:现在我创建了TWebBrowser WebBrowser1 的克隆,我称之为myW。它可以显示pdf,但我不知道如何正确删除它。

这是我创建它并显示 pdf 的代码:

  if (FileExists(LFileName))    // have the file so open it
   {
   try 
   {
   TWebBrowser *myW;
   myW = new TWebBrowser(Form1->Panel3);
   myW->Parent = Form1->Panel3;
   myW->Align = TAlignLayout::Client;
   myW->URL = "file://" + LFileName;
   myW->Visible = true;
   }
   catch ( const Exception& e )
   {
    ShowMessage(e.Message);
   }
  }

这是我删除它的尝试:

TComponent *T;
T = Form1->Panel3->Components[0];  // myW is only thing on Panel3
T->Free();  // not working
// T->DisposeOf(); // did not work

EDIT2:尝试处理临时TWebBrowser

我像这样创建TWebBrowser(它可以正常显示pdf):

   TWebBrowser *myW;
   myW = new TWebBrowser(Form1->Panel3);
   myW->Parent = Form1->Panel3;
   myW->Align = TAlignLayout::Client;
   myW->URL = "file://" + LFileName;  // displays the pdf
   myW->Visible = true;

然后我尝试像这样处理它但不起作用:

TComponent *T;
for (int i = 0; i < (Form1->Panel3->ComponentCount); i++) {
   T = Form1->Panel3->Components[i];
    if (TWebBrowser* TB = dynamic_cast<TWebBrowser*>(T))  {
        Form1->Panel3->RemoveComponent(TB);
        TB->Parent = nullptr;
        TB = nullptr;
        break;
      }
    }
}

我没有收到任何错误,我只是无法加载第二个 pdf(仍然找不到该文件的错误)。我正在使用演员表,因为我无法访问 T-&gt;Parent

【问题讨论】:

  • 您对TFileStream 的使用很好(尽管您的错误处理需要工作)。这听起来更像是一个TWebBrowser 错误。
  • T-&gt;Free(); 不能在 ARC 下工作,delete T; 也不能(这是你应该在 C++ 中使用的)。在 ARC 下调用 Free()/delete 与您刚刚完成 T = nullptr; 相同 - 它只会删除 TTWebBrowser 的引用,但不会删除 Panel3 的引用为OwnerParent。不过,T-&gt;DisposeOf() 应该可以工作。我建议在发布引用时更优雅一点,而 DisposeOf() 更暴力:T-&gt;Parent = nullptr; Form1-&gt;Panel3-&gt;RemoveComponent(T); T = nullptr;
  • 您发布的内容不是您问题的答案。您应该将其作为edit 发布到您的问题中。请这样做并删除答案。
  • 显然,问题出在TWebBrowser 本身。这就是为什么我强烈建议您根本不要使用它来显示您的 PDF,让操作系统为您显示它们。我确信iOS有办法做到这一点。 Windows 当然可以(ShellExecute())。如果用户没有安装 PDF 查看器,好吧。为您的应用设置一项要求。
  • 如果TWebBrowser 的单个实例无法加载多个文件,那么这显然是一个错误。随时report it to Embarcadero。另一方面,您确实需要验证您的动态TWebBrowser 实际上是否被正确销毁。尝试从TWebBrowser 派生一个类并覆盖它的析构函数,确保它在你期望的时候被调用。

标签: firemonkey indy c++builder-10.3-rio


【解决方案1】:
void showPaper()
{
// paperName (e.g. 22.pdf)

   UnicodeString LFileName = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetDocumentsPath(), paperNAME);
   if (!FileExists(LFileName)) { // file is not present so download it  
   UnicodeString URL = pdfURLv4 + paperNAME;  
   TFileStream* fs = new TFileStream(LFileName, fmCreate);
   Form1->Download->ConnectTimeout = 15000;  // give it 15 seconds
   Form1->Download->ReadTimeout = 15000;
   Form1->Download->Request->BasicAuthentication = true;
   Form1->Download->Request->Username = "XXXXXX";
   Form1->Download->Request->Password = "YYYYYY";
   Form1->Download->Request->UserAgent = "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0";
   try
   {
    Form1->Download->Get(URL, fs);
    Form1->Download->Disconnect();  // make sure socket is closed
   }
   catch(const System::Sysutils::Exception &)
   {
    try
    {
     UnicodeString URL = pdfURLv6 + paperNAME;  // the v6 url has brackets [] around host
     Form1->Download->Get(URL, fs);
     Form1->Download->Disconnect();  
    }
    catch(const System::Sysutils::Exception &)
    {
        ShowMessage(L"No/poor internet connection.");
        Form1->Download->Disconnect();  
        delete fs;
        return;
    }
}
delete fs;
} // end of download if block



//////// this gets around the wierd file lock issue with TWebBrowser
 UnicodeString TFileName = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetDocumentsPath(), "dummy.pdf");
 if (FileExists(TFileName)) {
  TFile::Delete(TFileName); // delete it if present
 }
 TFile::Copy(LFileName, TFileName);
//--------------------------------------






if (FileExists(LFileName))    // have the file so open it
 {
 try 
 {
   TWebBrowser *myW;
   myW = new TWebBrowser(Form1->Panel3);
   myW->Parent = Form1->Panel3;
   myW->Align = TAlignLayout::Client;
   myW->URL = "file://" + TFileName;
   myW->Visible = true;
 }
 catch ( const Exception& e )
 {
  ShowMessage(e.Message);
 }
  ShowMessage(Form1->WebBrowser1->URL);
 }
} // end of showPaper()

在我显示文件并且用户点击关闭按钮后,我这样做是为了摆脱临时的TWebBrowser

TComponent *T;
for (int i = 0; i < (Form1->Panel3->ComponentCount); i++) {
   T = Form1->Panel3->Components[i];
    //if (T->ClassName() == "TWebBrowser") {
    if (TWebBrowser* TB = dynamic_cast<TWebBrowser*>(T))  {
        Form1->Panel3->RemoveComponent(TB);
        TB->Parent = nullptr;
        TB = nullptr;
        break;
      }
}

【讨论】:

  • 你根本不需要if (T-&gt;ClassName() == "TWebBrowser")dynamic_cast 返回 nullptr 如果转换失败,请改用它。当您找到 TWebBrowser 时,您也不是来自循环的 break'ing
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多