URI Доступ к отчету CSV - PullRequest
       12

URI Доступ к отчету CSV

3 голосов
/ 18 октября 2019

Я новичок в C # и очень зеленый! У меня есть приложение C #, которое я хотел бы загрузить отчет из Reserved.ReportViewerWebControl.axd и сохранить его в определенном месте, я нашел этот код

var theURL = "http://TEST/TEST/Pages/TEST.aspx?&FileName=TEST&rs:Command=GetResourceContents";

        WebClient Client = new WebClient
        {
            UseDefaultCredentials = true
        };

        byte[] myDataBuffer = Client.DownloadData(theURL);

        var filename = "test.csv";
        var fileStructureLocal = @"C:\Users\%UserName%\TEST\Downloads".Replace("%UserName%", UserName);
        var fileStructureNetwork = "\\\\TEST\\TEST\\TEST\\TEST";

        var fileLocation = fileStructureLocal + "\\" + filename;

        if (System.IO.File.Exists(fileLocation) == true)
        {
            //DO NOTHING
        }
        else
        {
            System.IO.File.WriteAllBytes(fileLocation, myDataBuffer);
            //File.WriteAllBytes("c:\\temp\\report.pdf", myDataBuffer);
            //SAVE FILE HERE
        }

это работает, но я получаю исходный код, а не файл CSV. Я знаю, что URL-адрес, который я получаю при выполнении отчетов в обычном браузере, имеет идентификатор сеанса и идентификатор элемента управления. Я могу скопировать этот URL-адрес и поместить егона "theURL", и я получаю внутреннюю ошибку сервера 500. Я знаю, что я все перепутал, не уверен, что мне нужно делать, но я пробую много вещей. Это было самое близкое, что я получил ... lol Грустно, я знаю. ЭтоURL-адрес, который я получаю, когда выполняю его в браузере.

http://test/test/Reserved.ReportViewerWebControl.axd?%2fReportSession=brhxbx55ngxdhp3zvk5bjmv3&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=fa0acf3c777540c5b389d67737b1f866&OpType=Export&FileName=test&ContentDisposition=OnlyHtmlInline&Format=CSV

Как получить этот файл, чтобы загрузить файл с помощью нажатия кнопки в моем приложении и сохранить его в моем местоположении.

1 Ответ

3 голосов
/ 21 октября 2019

Ваша целевая веб-страница использует элемент управления SSRS ReportViewer для управления отображением отчетов. Этот элемент управления в значительной степени опирается на Состояние сеанса ASP.Net для отображения отчета в фоновом режиме с помощью вызовов Reserved.ReportViewerWebControl.axdобработчик ресурсов.

Это означает, что для использования указанной axd ссылки, которую вы определили, вы должны сначала инициировать создание и кэширование контента в контексте сеанса, прежде чем его можно будет загрузить, а затем вы должны загрузить его из тот же контекст.

  • Мы не можем просто запустить страницу один раз и выяснить URL-адрес, мы должны найти способ сделать это программно, используя один и тот же сеанс между запросами.

Элемент управления ReportViewer делает это с помощью javascript при нажатии кнопки загрузки, что означает отсутствие простой ссылки на Reserved.ReportViewerWebControl.axd для удаления из HTML. Это означает, что мы должны выполнить тот же сценарий вручную или смоделировать пользователя, щелкающего по ссылке.

В этом решении будут использованы некоторые методы очистки экрана (UX Automation) для имитации нажатия кнопки экспорта и захватарезультат, но я бы избегал этого, если бы вы могли.

Вы действительно должны попытаться связаться с разработчиком напрямую для получения рекомендаций, возможно, они реализовали некоторые простые параметры URL для экспорта напрямую без необходимости автоматизации интерфейса.

Концепция относительно проста:

  1. Создание сеанса веб-браузера на странице отчета
  2. Нажмите кнопку экспорта в CSV
    • это попытается открыть еще одну ссылку в новом окне, которую мы должны отключить!
  3. Захватить URL из нового окна
  4. Скачать файл экспортаиспользуя тот же контекст сеанса
    • Мы не можем использовать элемент управления веб-браузера для этого, потому что его интерфейс управляется пользовательским интерфейсом.

Мы не можем использовать HttpWebRequest или WebClient для выполнения javascript для HTMl DOM, для этого нужно использовать веб-браузер. Другая проблема, которая возникает, заключается в том, что мы не можем просто использовать события NewWindow или FileDownload WebBrowser в элементе управления, поскольку эти события не предоставляют такую ​​информацию, как URL-адрес для новых окон, источник загрузки файла или цель. Вместо этого мы должны ссылаться на внутренний COM-браузер (фактически IE) и использовать собственное событие NewWindow3 для захвата URL-адреса на Reserved.ReportViewerWebControl.axd, чтобы мы могли загрузить его вручную.

Я использую эти основные ссылки, чтобы объяснить методику

Наконецкак я упоминал выше, мы не можем использовать веб-браузер для прямой загрузки файла с URL-адреса, так как он откроет диалоговое окно SAVE AS в новом веб-браузере или сохранит его непосредственно в настроенную папку «Загрузки». Как описано в справочной статье, мы используем метод GetGlobalCookies от Эрики Чинчио, который можно найти в отличной статье, предоставленной @Pedro Leonardo (доступно здесь )

Я положил всеэто простое консольное приложение, которое вы можете запустить, просто измените URL вашего отчета, название ссылки экспорта и путь сохранения:

Ниже показано, как я получил ссылку, которую я хотел скачать,Точный заголовок и состав ссылки будут варьироваться в зависимости от реализации:

Locating the export link

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        SaveReportToDisk("http://localhost:13933/reports/sqlversioninfo", "CSV (comma delimited)", "C:\\temp\\reportDump.csv");
    }

    /// <summary>
    /// Automate clicking on the 'Save As' drop down menu in a report viewer control embedded at the specified URL
    /// </summary>
    /// <param name="sourceURL">URL that the report viewer control is hosted on</param>
    /// <param name="linkTitle">Title of the export option that you want to automate</param>
    /// <param name="savepath">The local path to save to exported report to</param>
    static void SaveReportToDisk(string sourceURL, string linkTitle, string savepath)
    {
        WebBrowser wb = new WebBrowser();
        wb.ScrollBarsEnabled = false;
        wb.ScriptErrorsSuppressed = true;
        wb.Navigate(sourceURL);

        //wait for the page to load
        while (wb.ReadyState != WebBrowserReadyState.Complete) { Application.DoEvents(); }

        // We want to find the Link that is the export to CSV menu item and click it
        // this is the first link on the page that has a title='CSV', modify this search if your link is different.
        // TODO: modify this selection mechanism to suit your needs, the following is very crude
        var exportLink = wb.Document.GetElementsByTagName("a")
                                    .OfType<HtmlElement>()
                                    .FirstOrDefault(x => (x.GetAttribute("title")?.Equals(linkTitle, StringComparison.OrdinalIgnoreCase)).GetValueOrDefault());
        if (exportLink == null)
            throw new NotSupportedException("Url did not resolve to a valid Report Viewer web Document");

        bool fileDownloaded = false;
        // listen for new window, using the COM wrapper so we can capture the url
        (wb.ActiveXInstance as SHDocVw.WebBrowser).NewWindow3 +=
            (ref object ppDisp, ref bool Cancel, uint dwFlags, string bstrUrlContext, string bstrUrl) =>
            {
                Cancel = true; //should block the default browser from opening the link in a new window
                Task.Run(async () =>
                {
                    await DownloadLinkAsync(bstrUrl, savepath);
                    fileDownloaded = true;
                }).Wait();
            };

        // execute the link
        exportLink.InvokeMember("click");

        //wait for the page to refresh
        while (!fileDownloaded) { Application.DoEvents(); }

    }

    private static async Task DownloadLinkAsync(string documentLinkUrl, string savePath)
    {
        var documentLinkUri = new Uri(documentLinkUrl);
        var cookieString = GetGlobalCookies(documentLinkUri.AbsoluteUri);
        var cookieContainer = new CookieContainer();
        using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
        using (var client = new HttpClient(handler) { BaseAddress = documentLinkUri })
        {
            cookieContainer.SetCookies(documentLinkUri, cookieString);
            var response = await client.GetAsync(documentLinkUrl);
            if (response.IsSuccessStatusCode)
            {
                var stream = await response.Content.ReadAsStreamAsync();

                // Response can be saved from Stream
                using (Stream output = File.OpenWrite(savePath))
                {
                    stream.CopyTo(output);
                }
            }
        }
    }

    // from Erika Chinchio which can be found in the excellent article provided by @Pedro Leonardo (available here: http://www.codeproject.com/Tips/659004/Download-of-file-with-open-save-dialog-box),
    [System.Runtime.InteropServices.DllImport("wininet.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
    static extern bool InternetGetCookieEx(string pchURL, string pchCookieName,
System.Text.StringBuilder pchCookieData, ref uint pcchCookieData, int dwFlags, IntPtr lpReserved);

    const int INTERNET_COOKIE_HTTPONLY = 0x00002000;

    private static string GetGlobalCookies(string uri)
    {
        uint uiDataSize = 2048;
        var sbCookieData = new System.Text.StringBuilder((int)uiDataSize);
        if (InternetGetCookieEx(uri, null, sbCookieData, ref uiDataSize,
                INTERNET_COOKIE_HTTPONLY, IntPtr.Zero)
            &&
            sbCookieData.Length > 0)
        {
            return sbCookieData.ToString().Replace(";", ",");
        }
        return null;
    }
}

Причина, по которой я советую поговорить с разработчиком передСнижение размера экрана кроличьей норы заключается в том, что в качестве стандарта при использовании элемента управления для просмотра отчетов я всегда стараюсь реализовать SSRS собственные rc: и rs: параметры URL или по крайней мере убедиться, что я предоставляю способЧтобы экспортировать отчеты напрямую через URL.

Вы не можете использовать эти параметры "из коробки", они предназначены для использования при запросах SSRSСервер напрямую, чего нет в вашем примере.

Я не придумал это сам, не знаю, из какого ресурса я это узнал, но это означает, что есть шанс, что другие пришли к аналогичному выводу. Я реализую это главным образом, чтобы я мог использовать эти концепции в остальной части приложения. Но и в том, что касается отчетов, одной из причин, по которой мы выбираем SSRS и RDL в качестве решения для создания отчетов, является его универсальность, мы пишем определение отчетов, элементы управления позволяют пользователям использовать их по мере необходимости. Если мы ограничили возможность экспорта отчетов пользователем, мы действительно недостаточно использовали эту среду.

...