0

I have a list of reports page. I have a download button for each report. If I click 4-5 reports download button I only get the last one downloaded. Below you can see my code

public async Task<ActionResult> Download(ReportPage currentPage, string id)
{
    var token = RystadIdentity.Current.AuthenticatedUser.Token;

    try
    {
        DocumentRequestInput documentRequestInput = new DocumentRequestInput(int.Parse(id), 2);
        documentRequestInput.Add(token);
    }
    catch
    { }

    Report report = await RystadGlobal.api.GetReport(token, int.Parse(id));
    string ext = Path.GetExtension(report.fileName);
    byte[] byteArray = await RystadGlobal.api.DownloadReport(token, report.Id);

    if (ext.ToLower() == ".pdf")
    {
        var name = RystadIdentity.Current.AuthenticatedUser.UserInfo.name;
        var company = RystadIdentity.Current.AuthenticatedUser.UserInfo.company;

        try
        {
            byteArray = PdfWatermarker.Watermark(byteArray, name, company);
        }
        catch (Exception ex)
        {
            //Ignore if failed and give the user the pdf anyway
        }
    }

    return File(byteArray, System.Net.Mime.MediaTypeNames.Application.Octet, report.fileName);
}

public async Task<byte[]> DownloadReport(Token token, int reportId)
{
    clientForDownloading.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token.token_type, token.access_token);

    var result = await clientForDownloading.GetAsync("api/" + ApiVersion + "/LibraryDocument/" + reportId + "/download");

    if (!result.IsSuccessStatusCode)
        throw new ApiException("Could not download the report");

    var contentBytes = await result.Content.ReadAsByteArrayAsync();

    return SevenZipHelper.Decompress(contentBytes);
}


public async Task<Report> GetReport(Token token, int reportId)
{
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token.token_type, token.access_token);

    var result = await client.GetAsync("api/" + ApiVersion + "/LibraryDocument/" + reportId);

    if (!result.IsSuccessStatusCode)
        throw new ApiException("Could not get report");

    Report report = await result.Content.ReadAsAsync<Report>();

    return report;
}   

Does anyone see the issue which I am missing?

Ted Nyberg
  • 7,001
  • 7
  • 41
  • 72
mohsinali1317
  • 4,255
  • 9
  • 46
  • 85
  • try doing it syncronously. `Report report = RystadGlobal.api.GetReport(token,int.Parse(id)).Result;` and `byte[] byteArray = RystadGlobal.api.DownloadReport(token, report.Id).Result;` – Pierre Jul 24 '18 at 08:42
  • If you are reusing the same instance of the `HttpClient` between requests (`clientForDownloading`, `client`), you are incorrectly overwriting authentication headers for the shared instance. The correct way of handling different headers when a shared http client is used - https://stackoverflow.com/questions/37928543/httpclient-single-instance-with-different-authentication-headers. – Wiktor Zychla Jul 24 '18 at 09:21
  • @WiktorZychla thanks for the input. I will fix it but I am assuming this is an another issue and not related to the download issue I am having. – mohsinali1317 Jul 24 '18 at 09:26
  • You need a [mcve] - can you give us one? – Enigmativity Jul 29 '18 at 12:53

1 Answers1

0

You can try to do it Synchronously like so:

public ActionResult Download(ReportPage currentPage, string id)
{
    var token = RystadIdentity.Current.AuthenticatedUser.Token;

    try
    {
        DocumentRequestInput documentRequestInput = new DocumentRequestInput(int.Parse(id), 2);
        documentRequestInput.Add(token);
    }
    catch
    { }

    Report report = RystadGlobal.api.GetReport(token, int.Parse(id)).Result;
    string ext = Path.GetExtension(report.fileName);
    byte[] byteArray = RystadGlobal.api.DownloadReport(token, report.Id).Result;

    if (ext.ToLower() == ".pdf")
    {
        var name = RystadIdentity.Current.AuthenticatedUser.UserInfo.name;
        var company = RystadIdentity.Current.AuthenticatedUser.UserInfo.company;

        try
        {
            byteArray = PdfWatermarker.Watermark(byteArray, name, company);
        }
        catch (Exception ex)
        {
            //Ignore if failed and give the user the pdf anyway
        }
    }

    return File(byteArray, System.Net.Mime.MediaTypeNames.Application.Octet, report.fileName);
}

The other problem might be in this method:

RystadGlobal.api.GetReport(token, int.Parse(id))

I do not know that this method looks like inside

EDIT

Try creating new instances of your clients in the methods you added in your question:

public async Task<byte[]> DownloadReport(Token token, int reportId)
{
    using(var clientForDL = new System.Net.Http.HttpClient())
    {    
        clientForDL.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token.token_type, token.access_token);

        var result = await clientForDL.GetAsync("api/" + ApiVersion + "/LibraryDocument/" + reportId + "/download");

        if (!result.IsSuccessStatusCode)
            throw new ApiException("Could not download the report");

        var contentBytes = await result.Content.ReadAsByteArrayAsync();

        return SevenZipHelper.Decompress(contentBytes);
    }
}


public async Task<Report> GetReport(Token token, int reportId)
{
    using(var clientForGet = new System.Net.Http.HttpClient())
    {
        clientForGet.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token.token_type, token.access_token);

        var result = await clientForGet.GetAsync("api/" + ApiVersion + "/LibraryDocument/" + reportId);

        if (!result.IsSuccessStatusCode)
            throw new ApiException("Could not get report");

        Report report = await result.Content.ReadAsAsync<Report>();

        return report;
    }
}
Pierre
  • 8,397
  • 4
  • 64
  • 80
  • This doesn't work. The download button gets stuck in a loop of sorts. I would have to do it with async. Is it not possible? – mohsinali1317 Jul 24 '18 at 08:53
  • @mohsinali1317 It is possible. But it seems like the problem lies with the `RystadGlobal.api.GetReport` method or the `RystadGlobal.api.DownloadReport` method then. – Pierre Jul 24 '18 at 09:02
  • I have added those two functions as well now. Can you see that please? – mohsinali1317 Jul 24 '18 at 09:12
  • @mohsinali1317 I saw your methods. Try creating new instances of `clientForDownloading` and `client`. Will edit my answer shortly – Pierre Jul 24 '18 at 09:34
  • It doesn't work. I am not doing .Result for the api end points. If I do that the calls gets stuck. When I remove it does the same thing. Download the last one. – mohsinali1317 Jul 24 '18 at 10:39
  • What you suggest here, creating new instances of the `HttpClient` is considered a bad antipattern https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ Fix this please, otherwise you are recommending an invalid approach. – Wiktor Zychla Jul 25 '18 at 11:08
  • @WiktorZychla AFAIK, web is stateless. Each user downloading a PDF via a webservice like the one OP asked about. Multiple sessions could in fact mess up a static singleton client. What you are suggesting is for a single application doing multiple calls to a webservice to use the HttpClient singleton. Thus the `using` statement will release the server memory or when the user closes it's browser and the session times out – Pierre Jul 25 '18 at 14:48
  • @Pierre: read the linked article (not mine) to learn why using the static singleton for the `HttpClient` is correct, even though it seems to contradict the common sense. In fact, you should always use the singleton in the concurrent environment and you should never dispose it, otherwise you leak resources. If you are still not convinced, read the article or other sources that confirm it. – Wiktor Zychla Jul 26 '18 at 10:42