I've seen a couple other posts here about export performance, but they both refer to relatively large amounts of data. I am having issues so I stripped my example down to the where it's exporting a blank spreadsheet (see code below) but it still takes over 10 seconds running in debug on my laptop. I'm using this in the context of a Blazor app and since this is synchronous the page hangs while the export is running. What can I do to speed this up?
Workbook workbook = new Workbook();
Worksheet worksheet = workbook.Worksheets.Add();
IWorkbookFormatProvider formatProvider = new XlsxFormatProvider();
using MemoryStream ms = new();
formatProvider.Export(workbook, ms, new TimeSpan(0, 0, 30)); // This takes over 10 seconds
_fileData = ms.ToArray(); // byte array that the Blazor component reads
3 Answers, 1 is accepted
Hello, Doug,
I am sorry to hear that you are facing any performance difficulties with the SpreadProcessing libraries.
Following the provided information, I was unable to reproduce the issue you are facing. Please refer to the screenshot below illustrating the measured time on my end with the latest version:
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
Workbook workbook= new Workbook();
Worksheet worksheet = workbook.Worksheets.Add();
IWorkbookFormatProvider formatProvider = new XlsxFormatProvider();
using MemoryStream ms = new();
formatProvider.Export(workbook, ms, new TimeSpan(0, 0, 30)); // This takes over 10 seconds
byte[] _fileData = ms.ToArray(); // byte array that the Blazor component reads
sw.Stop();
Debug.WriteLine($"Elapsed time: {sw.ElapsedMilliseconds} ms");
}
I have attached my sample project. Please give it a try and see how it works on your end. Am I missing something? Could you please specify the exact steps how to reproduce the problem? Feel free to modify the project in a way to reproduce the experienced issue and get back to me with it so I can investigate the precise case. Thank you in advance.
Regards,
Dess | Tech Support Engineer, Principal
Progress Telerik
Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.
Dess,
Thanks for the quick response. Running your console app I get the same results you do. So I created a new Blazor WebAssembly standalone app (attached) and pasted the same code into the OnInitializedAsync method of the Home page. Running it that way the export now takes a long time. So apparently there's something about it being incorporated into a Blazor app that's interfering with the export. Any ideas?
Hi, Doug,
Thank you for confirming the results in both environments and for clarifying that the performance issue is specific to Blazor WebAssembly.
Indeed, the same code takes more time when it is run in a WASM app: <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
After discussing the case with the team and putting some efforts to measure the time for all the internal parts of the creation process of a workbook and exporting it, we have come to a conclusion that half of the time (4-5 seconds) comes from the document processing part. The rest of the time (from 4-5s to 10s) comes from the WASM itself and we do not have any control on it:
Why Export Is Slow in Blazor WebAssembly
- Blazor WebAssembly runs entirely in the browser, which imposes significant performance and memory constraints compared to running on a desktop or server.
- The .NET runtime in WebAssembly is slower for CPU- and memory-intensive operations, such as exporting even a simple XLSX file, due to the browser sandbox and the nature of WebAssembly execution.
- This performance limitation is expected for complex libraries like Telerik Document Processing when used client-side in WebAssembly.
Optimization Possibilities in Blazor WebAssembly
- There are currently no specific settings or optimizations in the Document Processing libraries that will significantly improve export speed within Blazor WebAssembly. We already have two public items related to WASM. You can cast your vote for the item, track its progress, subscribe for status changes, and add your comments on the following links:
SpreadProcessing: Slow performance when auto fitting rows/columns in WASM
- Keeping the export logic as simple as possible and minimizing data size can help, but the gains are generally marginal in WebAssembly.
- Offloading heavy processing from the UI thread (e.g., using Task.Run) does not improve the situation in Blazor WebAssembly, as all code executes on a single thread in the browser.
Recommended Approaches
- Server-Side Export: The best way to achieve fast XLSX export is to move the export operation to the server (Blazor Server or a Web API), then transfer the generated file to the client for download.
- RadSpreadStreamProcessing: For scenarios involving large data exports, consider using RadSpreadStreamProcessing, which is optimized for high-performance, low-memory XLSX export.
- https://docs.telerik.com/devtools/document-processing/libraries/radspreadstreamprocessing/overview
https://github.com/telerik/document-processing-sdk/tree/master/SpreadStreamProcessing/GenerateDocument//
Questions and Next Steps
Is client-side export within Blazor WebAssembly a strict requirement for your application, or could you consider moving the export to the server?
I hope you find this information helpful. Please, let me know if there is anything else I can assist you with.
Regards,
Dess | Tech Support Engineer, Principal
Progress Telerik
Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.
Dess,
Thank you for all of that information. It's very helpful. I am leaving on vacation for a week shortly but I'll plan to move the export into a Web API so that should resolve the problem. I did play around with the RadSpreadStreamProcessing but I couldn't get it working with a Blazor TelerikSpreadsheet (at least not in a reasonable amount of time), so do you have any examples where RadSpreadStreamProcessing is used with a Blazor TelerikSpreadsheet?
Hi, Doug,
We do not have a dedicated set of examples where RadSpreadStreamProcessing is used with the Spreadsheet component offered by the Telerik UI for Blazor suite. Could you please provide more details what is the exact requirement you are trying to achieve? What difficulties you are facing? Once we get better understanding of the precise case, we would be able to think about an appropriate solution and provide further assistance. Thank you in advance.
Please ensure first that you are using the .NET Standard version of the Document Processing Libraries: The assemblies/packages for .NET Standard do not contain the word Windows in their name.
After reviewing the available Blazor demoes, I have found the following which is related to integration with DPL:
Spreadsheet - Tools - this example shows how to define a tool set with a custom tool order and a custom tool that integrates the Spreadsheet with Telerik RadSpreadProcessing to modify the loaded Excel file programmatically. In this case, RadSpreadProcessing freezes the top rows and first column in the first sheet.
I would like to note that RadSpreadProcessing and RadSpreadStreamProcessing libraries internally follow different core logic which serve different purposes.
The main differences between the two spreadsheet processing libraries include:
- RadSpreadStreamProcessing writes directly into a stream, while RadSpreadProcessing creates models for the elements in the document. This is why the spread streaming library uses significantly less memory than RadSpreadProcessing.
- RadSpreadStreamProcessing does not perform any formula or other layout-related calculations, which makes its file generation performance much better compared to RadSpreadProcessing.
It is appropriate to use the RadSpreadStreamProcessing library to create or read large amount of data with a low memory footprint and great performance. You can also append data to an already existing document stream. The generated document can be exported directly to a file on the file system or to a stream (for example, to send it to the client).
I hope you will find this information helpful. Should you have further questions please let me know.
Dess,
I'll have to get back to you on specific use case for RadSpreadStreamProcessing but for now I'll just plan to use RadSpreadProcessing. As you suggested previously, I created an API to do the export on the server, and that works well when my API doesn't take any parameters/payload, and I just build my starting point for the spreadsheet and return a byte array. However when I want to send a Workbook to the API (serialized as json) and export that to a byte array, .NET is kicking it back with a 400 and my code doesn't get reached. Is there a trick to sending a workbook serialized as json?
Error:
System.Exception: {"errors":{"ActiveSheet":["Error setting value to 'ActiveSheet' on 'Telerik.Windows.Documents.Spreadsheet.Model.Workbook'."],"ActiveWorksheet":["Error setting value to 'ActiveWorksheet' on 'Telerik.Windows.Documents.Spreadsheet.Model.Workbook'."]},"type":"https://tools.ietf.org/html/rfc9110#section-15.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"00-3bc6054ed9bbb6f001a9bfcc47d13341-8a05fa7377341d57-00"}
My API code:
[HttpPost("ExportPopulatedWorkbook")]
public ActionResult<byte[]> ExportPopulatedWorkbook([FromBody] Workbook workbook)
{
using MemoryStream ms = new();
XlsxFormatProvider formatProvider = new();
formatProvider.Export(workbook, ms, new TimeSpan(0, 0, 30));
return ms.ToArray();
}
Hello, Doug,
According to the provided information, most probably the error occurs because the Workbook object from RadSpreadProcessing is not designed to be serialized or deserialized with JSON. Are you using a custom logic for it?
The model contains complex references, lacks parameterless constructors for some properties, and exposes read-only members like ActiveSheet and ActiveWorksheet, which prevents JSON serializers from reconstructing it correctly. This is why your API throws a 400 error before your code executes.
I would highly encourage you to transfer the workbook data as a file (such as XLSX), or send a simplified data structure (DTO) and reconstruct the Workbook on the server.
Option 1: Transfer as XLSX File or Byte Array
Send the XLSX content as a byte array from the client. On the server, use XlsxFormatProvider.Import to load the workbook, then process/export as needed.
Option 2: Transfer a DTO or Plain Data
If you need to transfer structured data (not a file), define a DTO that contains only the necessary data (for example, sheet names, cell values, etc.). On the server, construct a new Workbook from this DTO.
It is up to you what approach to choose. I hope you find this information helpful.
Regards,
Dess | Tech Support Engineer, Principal
Progress Telerik
Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.
Dess,
Thank you for the information regarding serialization of the workbook. I moved all of my validation code into the API and am now only passing byte arrays to and from the API and that is working well. Thanks for your help!