Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document Viewer Not loading with .pptx and editable pdfs #17

Closed
tintin215 opened this issue Dec 6, 2024 · 9 comments
Closed

Document Viewer Not loading with .pptx and editable pdfs #17

tintin215 opened this issue Dec 6, 2024 · 9 comments

Comments

@tintin215
Copy link

tintin215 commented Dec 6, 2024

Hi,

We have encountered an issue with the document viewer when trying to render the body of the document viewer by using ComponentRenderer.RenderBody, particularly when using .pptx and editable pdfs. The component does not return a result and keeps throwing The thread '[Thread Destroyed]' (xxxx) has exited with code 0 (0x0). in the background. This only happens when we use cloud based caching like Azure Blob Storage. I have also tried using AmazonS3 for caching, but the component returns the attached image on all documents.

Screenshot 2024-12-06 094731

I have a self contained ASP.Net solution that you can run that demonstrates the issue but the attachment is too big to upload. Please let me know if you need the sample uploaded somewhere else. Can you please assist.

Please let me know if you need further details.

Thanks and Regards

Clinton Mercieca

@GleamTech
Copy link
Owner

Well, which version of DocumentUltimate are you using? From the screenshot I guess it's a recent version.

Why are you using undocumented ComponentRenderer.RenderBody method instead of platform specific RenderBody method? For example, in ASP.NET Core application you should use this.RenderBody in the razor page (.cshtml).

If you have to use ComponentRenderer.RenderBody for some reason, also make you sure you save the state before calling it:

var statefulComponent = component as StatefulComponent;
if (statefulComponent != null)
    ComponentRenderer.SaveState(statefulComponent);

var result = ComponentRenderer.RenderBody(component);

You would receive a "session expired" message if not.

But these errors:

The component does not return a result and keeps throwing The thread '[Thread Destroyed]' (xxxx) has exited with code 0 (0x0). in the background.

Cannot access a closed file

seems like they are about how you pass a FileProvider instance to DocumentViewer.Document property.
In exact, it seems you are passing a closed stream.

See docs for correct usage:
https://docs.gleamtech.com/documentultimate/html/P_GleamTech_DocumentUltimate_AspNet_UI_DocumentViewer_Document.htm

For example, if you are passing a stream:

//Setting a StreamFileProvider instance,
//to connect to a file in a Stream:
//Optional parameters dateModified and size: used for detailed file info, e.g. for generating better cache keys.
fileProvider = new StreamFileProvider(
    "SomeFile.ext", //file can also be set as a relative path "SomeFolder/SomeFile.ext".
    yourStream,
    yourFileDateModified, //Provide dateModified to prevent cache key conflicts.
    yourFileSize //Provide size only if your stream is not seekable to prevent cache key conflicts.
);

If yourStream is disposed, you would get an error like Cannot access a closed file. You can try to copy your stream to memory and pass that instead.

@tintin215
Copy link
Author

Hi,

Thanks for the prompt reply.

We are using the latest release of the component.

We're using ComponentRenderer.RenderBody because the method is in a separate library used to generate the DocumentViewer to be used in different UI interfaces.

The method ComponentRenderer.SaveState is being called.

Also I made sure the stream is open before trying to generate the DocumentViewer and everything seems in order.

This issue only presents itself when using Azure or AmazonS3 cloud storage.

Please let me know if you need more information or a sample that reproduces our problem.

Thanks in advance.

@GleamTech
Copy link
Owner

GleamTech commented Dec 9, 2024

Do you mean it happens when you use Amazon S3 for DocumentCache or when you open a file from Amazon S3? These are different, because when you open a file from Amazon S3, you should use file provider like below. If you manually open a stream from Amazon S3 and pass that to DocumentViewer, then that stream will not be available by the time DocumentViewer accesses it. DocumentViewer will access that file, after it's already rendered and displayed on the page, NOT at the time you call RenderBody. So you need a serializable file location that can be accessed later, FileSystemFileProvider exactly does that:

//Setting a FileSystemFileProvider instance with an AmazonS3Location instance,
//to connect to Amazon S3 cloud file system.
documentViewer.Document = new FileSystemFileProvider
{
    File = "Document.docx",
    Location = new AmazonS3Location
    {
        //Leave Path empty to connect to the root of the bucket. 
        //For connecting to subfolders, Path should be specified as a relative path (eg. "some/folder")
        //Path = "some/folder",

        BucketName = "BUCKET",
        Region = "eu-central-1",
        AccessKeyId = "XXX",
        SecretAccessKey = "XXX",
    }
};

@tintin215
Copy link
Author

Hi,

The file is being loaded from a MemoryStream, AmazonS3/Azure is used as the caching method.... The issue with Azure only happens with specific file types, .pptx and editable pdfs.

@GleamTech
Copy link
Owner

I have a self contained ASP.Net solution that you can run that demonstrates the issue but the attachment is too big to upload.

Can you delete bin and obj folders in that project and zip it and send it? Those folders should be causing the large size. Files there will be restored by NuGet any way so you can delete and send it.

@tintin215
Copy link
Author

Hi,

Sorry for the late reply. Please find the project attached.

DocumentViewer.zip

@GleamTech
Copy link
Owner

Ok, I replicated the problem and stalling problem occurs with only Azure Blob Storage (not Amazon S3) and only when you use .NET Framework (net472) version of DocumentUltimate DLLs (you have to use this version because your project is legacy Web Site project in net472). For newer project types that use netstandard2.0 version of DocumentUltimate DLLs, the stalling will not occur.

Back in v6.8.5 (April 7, 2023), we actually tried to fix this:

  • Fixed: With .NET Framework projects, AzureBlobFileSystem stalled (waited indefinitely) when creating empty files.
    This also caused DocumentCache to stall, as it tries to create empty .sourceName files.

It seems it's not completely fixed. So the problem is not with rendering of the DocumentViewer but rather with it trying to get info about file from cache before rendering, and as AzureBlobFileSystem stalls, the viewer never completes rendering.

I suspect the issue occurs as we internally use merged version of netstandard2.0 version of Microsoft.Azure.Storage.Blob package for both net472 and netstandard2.0 targets. We should separately use net472 version of Microsoft.Azure.Storage.Blob for net472 target.

We will release a fix shortly, I will let you know.

@tintin215
Copy link
Author

Thanks for looking into it

@GleamTech
Copy link
Owner

GleamTech commented Jan 7, 2025

Ok this is now fixed with DocumentUltimate v7.3.0 (which uses GleamTech.Common v6.3.0).

I also updated some code in your project:
DocumentViewer-Fixed.zip

In Default.aspx, added:

<asp:PlaceHolder ID="documentViewerPlaceHolder" runat="server" />

In Default.aspx.cs,

  • added
protected void Page_Load(object sender, EventArgs e)
{
	var viewer = GetDocumentViewer();
	documentViewerPlaceHolder.Controls.Add(viewer);
}
  • and changed GetDocumentViewer method to create and return a DocumentViewerControl instance rather than a DocumentViewer instance:
public static GleamTech.DocumentUltimate.AspNet.WebForms.DocumentViewerControl GetDocumentViewer()
{
    //Create a document viewer control programatically and set the document source and cache locations
    DocumentUltimateWebConfiguration webConfiguration = new DocumentUltimateWebConfiguration()
    {
        CacheLocation = new AzureBlobLocation
        {
            AccountKey = "DELETED",
            AccountName = "DELETED",
            Container = "documents",
            Path = "testing",
            UseHttps = true
        }
    };
    GleamTech.DocumentUltimate.AspNet.WebForms.DocumentViewerControl documentViewer = new GleamTech.DocumentUltimate.AspNet.WebForms.DocumentViewerControl(webConfiguration);

    string workingDirectory = Environment.CurrentDirectory;

    documentViewer.Document = new StreamFileProvider("Guinness GEI - Qrtly call - Covering 2024 Q3.pptx", new FileStream(AppDomain.CurrentDomain.BaseDirectory + "Guinness GEI - Qrtly call - Covering 2024 Q3.pptx", FileMode.Open, FileAccess.Read, FileShare.Read));
    documentViewer.DocumentOptions = new DocumentOptions() { DownloadFileName = "Guinness GEI - Qrtly call - Covering 2024 Q3.pptx" };

    //documentViewer.Document = new StreamFileProvider("FF deVere France new logo - Interactive Version.pdf", new FileStream(AppDomain.CurrentDomain.BaseDirectory + "FF deVere France new logo - Interactive Version.pdf", FileMode.Open, FileAccess.Read, FileShare.Read));
    //documentViewer.DocumentOptions = new DocumentOptions() { DownloadFileName = "FF deVere France new logo - Interactive Version.pdf" };

    documentViewer.SidebarVisible = false;
    documentViewer.ZoomMode = DocumentViewerZoomMode.PageWidth;
    documentViewer.ScrollMode = DocumentViewerScrollMode.Vertical;
    documentViewer.ProductInfoRendered = false;
    documentViewer.DisplayMode = DisplayMode.InPlace;
    documentViewer.Width = Unit.Percentage(100); //CssLength.Percentage(100);
    documentViewer.Height = Unit.Percentage(100); //CssLength.Percentage(100);
    documentViewer.DeniedPermissions = DocumentViewerPermissions.SelectText | DocumentViewerPermissions.FillForms;

    //return result
    return documentViewer;
}

DocumentViewerControl is for ASPX (ASP.NET Classic) and when you add it to a placeholder container, you can still create it programmatically and then add it. This way you will not need to mess with ComponentRenderer. The other class DocumentViewer is to be used mainly with ASP.NET Core.

When you run the project, you will see the second viewer is rendered at the bottom and it will be the one which is created by GetDocumentViewer method and which uses Azure Blob for document cache location.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants