Сообщений 2    Оценка 90        Оценить  
Система Orphus

Как сохранить содержимое текущего документа в файл?

(4 способа сделать это)

Автор: Тимофей Чадов
Опубликовано: 14.10.2001
Исправлено: 13.03.2005
Версия текста: 2.0


Способ 1. Интерфейс IOleCommandTarget
Способ 2. Интерфейс IPersistFile
Способ 3. Коллекция document.all
Cпособ 4. Коллекция document.images

Демонстрационная программа - 9 KB
Исходные тексты - 19 KB

Способ 1. Интерфейс IOleCommandTarget

Класс CHtmlView содержит метод ExecWB, который является оберткой для вызова метода Exec интерфейса IOleCommandTarget. Воспользовавшись им, можно очень легко отправить броузеру команду "Сохранить Как", что приведет к появлению стандартного диалога сохранения файлов HTML.

void CMyHtmlView::OnHtmlSaveAs() 
{
    ExecWB(OLECMDID_SAVEAS, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);    
}

Кстати, с помощью этого метода можно вызывать и другие команды Internet Explorer'a. Не обольщайтесь названием второй константы OLECMDEXECOPT_DONTPROMPTUSER - диалог сохранения файлов все равно будет выведен.

Итак, что мы получаем. С одной стороны, если все что нам требуется - дать возможность пользователю сохранять просматриваемые документы, - это безусловно лучший способ. Однако, если нам потребуется сохранить содержимое броузера незаметно для пользователя, придется воспользоваться одним из следующих методов.

Способ 2. Интерфейс IPersistFile

Если перед Вами стоит задача сохранять только текст, то самый, на мой взгляд, простой способ - воспользоваться методом Save интерфейса IPersistFile. С помощью него можно легко сохранить текст текущего документа в произвольный файл на диске.

void CMyHtmlView::OnHtmlSave() 
{
    LPDISPATCH pDispatch = GetHtmlDocument();        
    if(pDispatch)
    {
        IPersistFile* pFile;
        HRESULT hr = pDispatch->QueryInterface( __uuidof( IPersistFile ), (void**)&pFile );
        if (SUCCEEDED(hr))
        {        
            pFile->Save( L"с:\\myfile.htm", FALSE);
            pFile->Release();
        }
        pDispatch->Release();
    }
}

При сохранении текста документа через IPersistFile следует помнить, что если содержимое документа изменялось после загрузки, например, с использованием методов объектной модели DHTML, то изменения не будут запомнены. И в этом случае, вы наверняка, сохраните совсем не то, что предполагали. В этом случае, придется воспользоваться следующим способом.

Способ 3. Коллекция document.all

Объектная модель DHTML представляет множество способов воздействовать на содержимое документа, позволяя достаточно легко и удобно манипулировать практически любым элементом страницы. К числу особо важных коллекций DOM относится коллекция документа all. Она содержит все элементы страницы. То что надо для нашей задачи. Обратившись к свойству outerHTML мы получим полный текст документа в виде HTML, с учетом всех текущих изменений. Остается только сохранить его на диск.

void CMyHtmlView::HtmlSaveText( IHTMLDocument2* pDoc, CString sFileName)
// Сохранить текст документа pDoc в файл sFileName
{
    USES_CONVERSION;

    // Получаем содержимое коллекции all
    CComQIPtr< IHTMLElementCollection, &IID_IHTMLElementCollection> pAll;
    pDoc->get_all( &pAll );

    CComBSTR bsAll;
    CComVariant v1( 0), v2;    
    CComQIPtr<IDispatch, &IID_IDispatch> pDisp;    
    pAll->item( v1, v2, &pDisp);

    CComQIPtr<IHTMLElement, &IID_IHTMLElement> pElement;
    pElement = pDisp;
    pElement->get_outerHTML( &bsAll);

    // Сохраняем полученную строку на диск
    ofstream to( (LPCSTR)sFileName);
    if (to)
    {
        CString sHtml( W2A( bsAll));
        to.write( sHtml, sHtml.GetLength());
        to.close();
    }
}
Выбрав в качестве содержимого элемента pElement свойство outerText мы получим только текстовое представление текущего документа, без тэгов. Это свойство может быть использовано для сохранения HTML документа в текстовом виде.

Cпособ 4. Коллекция document.images

Казалось бы, все здорово. Мы уже научились сохранять текст 3-мя способами. Вот только одна загвоздка. А почему сохраненный документ выглядить так бледно? Куда делись все картинки? Опять приходится усложнять задачу. Ведь хочется сохранять не только текст, но и картинки.

Список изображений можно получить воспользовавшись коллекцией images объекта document. Эта коллекция содержит все тэги <IMG> документа. Таким образом, последовательно обращаясь к атрибуту src каждого элемента коллекции, мы получим текущее месторасположение всех изображений документа. А само изображение? А само изображение придется скачивать вручную.

Для этой цели вполне подойдет функция UrlDownloadToFile(). Конечно, вы можете использовать любой другой способ загрузки файла, например, поискав его в кэше IE. Но мои эксперименты с функцией GetUrlCacheEntryInfo не привели к надежному результату. Примерно, для половины рисунков возвращалась ошибка, хотя реально файлы во временном каталоге присутствовали. Кто знает в чем дело, пожалуйста, поделитесь. UrlDownloadToFile в этом смысле работает надежнее, к тому же, самостоятельно использует кэш. Кроме этого, реализовав callback интерфейс IBindStatusCallback можно непосредственно следить за процессом загрузки. Итак, файлы рисунков сохраняются, осталось перед сохранением текста поменять атрибут src всех тэгов <IMG>, указавав URL наших файлов. Все, теперь можно сохранить и текст.

void CMyHtmlView::SaveImages( IHTMLDocument2* pDoc, CString sPath)
// Сохранить все рисунки документа pDoc в каталог указанный в sPath
{
    USES_CONVERSION;

    // Получаем коллекцию тэгов <IMG>
    CComQIPtr<IHTMLElementCollection, &IID_IHTMLElementCollection> pImages;
    pDoc->get_images( &pImages);

    long nImages;
    pImages->get_length( &nImages);
    for (int i=0; i<nImages; i++)
    {
        CComVariant v1( i), v2;    
        CComQIPtr<IDispatch, &IID_IDispatch> pDisp;
        CComQIPtr<IHTMLImgElement, &IID_IHTMLImgElement> pImg;
        pImages->item( v1, v2, &pDisp);
        pImg = pDisp;
        
        // Получаем атрибут src
        CComBSTR bstr; 
        pImg->get_src( &bstr);

        CString fname;
        fname.Format( "%simg_%d.gif", sPath, i);
        
        // Скачиваем файл
        if( SUCCEEDED( URLDownloadToFile( 0, W2A( bstr), fname, 0, 0) ) )
        {
            // Меняем атрибут src на новый
            bstr = fname;
            pImg->put_src( bstr.Copy());
        }
    }    
}

Ну вот, вроде все работает. Конечно, верхом красоты и изящества этот способ не назовешь, но для некоторых приложений вполне подходит. Среди замеченных недостатков, можно отметить тот факт, что UrlDownloadToFile не работает с протоколом Res, т.е. рисунки из ресурсов так просто достать не удастся. Это, конечно не проблема, поэтому оставляем ее читателям в качестве самостоятельной тренировки. В общем, нет пределов совершенства.

Дерзайте!


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 2    Оценка 90        Оценить