Descargar XLSX declaración AEAT (modelo 303/347) desde ASP.NET sin Office | IronXL
Implementar la funcionalidad de descarga de ficheros XLSX desde una aplicación ASP.NET es un requisito habitual para los departamentos de cumplimiento fiscal en España: desde generar el fichero de la declaración trimestral de IVA (modelo 303) hasta exportar el listado de operaciones con terceros para el modelo 347 de la AEAT. Sin embargo, el proceso manual de generar y entregar estos archivos introduce complejidad técnica significativa: gestión de MemoryStream, configuración de cabeceras HTTP de disposición de contenido y, sobre todo, la necesidad de hacerlo sin instalar Microsoft Office en los servidores de producción —requisito habitual en infraestructuras Linux y Docker de AEAT, Banco de España / CNMV y otras entidades supervisadas.
Este artículo explica cómo crear y descargar ficheros Excel en ASP.NET C# MVC utilizando la biblioteca IronXL. Cubriremos la exportación de datos a ficheros XLSX reales para declaraciones AEAT (modelos 303, 347, 390), la conversión de subidas de archivos para re-exportación, y la devolución de descargas al navegador —todo sin instalar Microsoft Office ni depender de Excel Interop. Instala el paquete NuGet de IronXL y empieza una prueba gratuita para seguir los ejemplos de código.
¿Cómo crear y descargar un fichero XLSX de declaración AEAT desde un controlador?
El patrón principal para descargar un fichero Excel XLSX de una declaración AEAT (modelo 303) en ASP.NET Core consta de tres pasos: crear el libro, escribirlo en un flujo y devolver el flujo como respuesta de archivo. El código siguiente muestra una acción de controlador MVC completa que genera un XLSX con datos de IVA y lo envía al navegador —sin necesidad de Microsoft Office en el servidor.
using IronXL;
using Microsoft.AspNetCore.Mvc;
public class ReportController : Controller
{
// Export employee data as a downloadable Excel file
public IActionResult DownloadExcel()
{
// Create a new workbook and worksheet
var workbook = WorkBook.Create(ExcelFileFormat.XLSX);
var worksheet = workbook.CreateWorkSheet("Employees");
// Add header row with column names
worksheet["A1"].Value = "Name";
worksheet["B1"].Value = "Department";
worksheet["C1"].Value = "Salary";
// Populate cells with data values
worksheet["A2"].Value = "Alice Torres";
worksheet["B2"].Value = "Engineering";
worksheet["C2"].Value = 95000;
worksheet["A3"].Value = "James Park";
worksheet["B3"].Value = "Marketing";
worksheet["C3"].Value = 78000;
// Export workbook to a MemoryStream for download
var stream = workbook.ToStream();
string filename = "Employees.xlsx";
var content = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
// Return file to the browser with content disposition attachment
return File(stream, content, filename);
}
}
using IronXL;
using Microsoft.AspNetCore.Mvc;
public class ReportController : Controller
{
// Export employee data as a downloadable Excel file
public IActionResult DownloadExcel()
{
// Create a new workbook and worksheet
var workbook = WorkBook.Create(ExcelFileFormat.XLSX);
var worksheet = workbook.CreateWorkSheet("Employees");
// Add header row with column names
worksheet["A1"].Value = "Name";
worksheet["B1"].Value = "Department";
worksheet["C1"].Value = "Salary";
// Populate cells with data values
worksheet["A2"].Value = "Alice Torres";
worksheet["B2"].Value = "Engineering";
worksheet["C2"].Value = 95000;
worksheet["A3"].Value = "James Park";
worksheet["B3"].Value = "Marketing";
worksheet["C3"].Value = 78000;
// Export workbook to a MemoryStream for download
var stream = workbook.ToStream();
string filename = "Employees.xlsx";
var content = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
// Return file to the browser with content disposition attachment
return File(stream, content, filename);
}
}
Imports IronXL
Imports Microsoft.AspNetCore.Mvc
Public Class ReportController
Inherits Controller
' Export employee data as a downloadable Excel file
Public Function DownloadExcel() As IActionResult
' Create a new workbook and worksheet
Dim workbook = WorkBook.Create(ExcelFileFormat.XLSX)
Dim worksheet = workbook.CreateWorkSheet("Employees")
' Add header row with column names
worksheet("A1").Value = "Name"
worksheet("B1").Value = "Department"
worksheet("C1").Value = "Salary"
' Populate cells with data values
worksheet("A2").Value = "Alice Torres"
worksheet("B2").Value = "Engineering"
worksheet("C2").Value = 95000
worksheet("A3").Value = "James Park"
worksheet("B3").Value = "Marketing"
worksheet("C3").Value = 78000
' Export workbook to a MemoryStream for download
Dim stream = workbook.ToStream()
Dim filename As String = "Employees.xlsx"
Dim content = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
' Return file to the browser with content disposition attachment
Return File(stream, content, filename)
End Function
End Class
Archivo de Excel de salida

El método WorkBook.Create genera un nuevo documento Excel, y CreateWorkSheet añade la primera hoja. Tras rellenar las celdas con valores, el método ToStream convierte el libro completo en un MemoryStream —ningún archivo temporal toca el disco, lo que es esencial en entornos Docker y servidores Linux donde el almacenamiento es efímero. El método de retorno File() del controlador establece la cabecera de disposición de contenido como attachment, indicando al navegador que descargue el fichero. El tercer parámetro controla el nombre del archivo en el diálogo de descarga del usuario.
Este patrón funciona tanto en .NET Framework como en proyectos ASP.NET Core, lo que lo convierte en el enfoque de referencia para aplicaciones web que necesitan exportar datos a Excel, incluyendo la generación de ficheros XLSX para la sede electrónica de la AEAT desde servidores Linux sin Office.
¿Cómo exportar datos de base de datos a un XLSX de declaración AEAT descargable?
La mayoría de los escenarios reales implican exportar datos de una tabla de base de datos, no valores codificados. Un caso típico en el contexto del SII (Suministro Inmediato de Información) es consultar los registros de facturas emitidas y recibirlas del trimestre, y generar el XLSX del modelo 303 para su descarga inmediata desde la aplicación de gestión interna —sin Office en el servidor de producción.
using IronXL;
using Microsoft.AspNetCore.Mvc;
public class DataExportController : Controller
{
// Export database records to an xlsx file for download
public IActionResult ExportDatabaseReport()
{
// Simulate a database query returning order records
var orders = new[]
{
new { OrderId = 1001, Customer = "Acme Corp", Total = 4500.00m },
new { OrderId = 1002, Customer = "Globex Inc", Total = 12300.50m },
new { OrderId = 1003, Customer = "Initech LLC", Total = 890.75m },
};
var workbook = WorkBook.Create(ExcelFileFormat.XLSX);
var worksheet = workbook.CreateWorkSheet("Orders");
// Write headers
worksheet["A1"].Value = "Order ID";
worksheet["B1"].Value = "Customer";
worksheet["C1"].Value = "Total";
// Write each row of data from the query results
for (int i = 0; i < orders.Length; i++)
{
int row = i + 2;
worksheet[$"A{row}"].Value = orders[i].OrderId;
worksheet[$"B{row}"].Value = orders[i].Customer;
worksheet[$"C{row}"].Value = orders[i].Total;
}
var stream = workbook.ToStream();
string filename = $"OrderReport-{DateTime.Now:yyyyMMdd}.xlsx";
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename);
}
}
using IronXL;
using Microsoft.AspNetCore.Mvc;
public class DataExportController : Controller
{
// Export database records to an xlsx file for download
public IActionResult ExportDatabaseReport()
{
// Simulate a database query returning order records
var orders = new[]
{
new { OrderId = 1001, Customer = "Acme Corp", Total = 4500.00m },
new { OrderId = 1002, Customer = "Globex Inc", Total = 12300.50m },
new { OrderId = 1003, Customer = "Initech LLC", Total = 890.75m },
};
var workbook = WorkBook.Create(ExcelFileFormat.XLSX);
var worksheet = workbook.CreateWorkSheet("Orders");
// Write headers
worksheet["A1"].Value = "Order ID";
worksheet["B1"].Value = "Customer";
worksheet["C1"].Value = "Total";
// Write each row of data from the query results
for (int i = 0; i < orders.Length; i++)
{
int row = i + 2;
worksheet[$"A{row}"].Value = orders[i].OrderId;
worksheet[$"B{row}"].Value = orders[i].Customer;
worksheet[$"C{row}"].Value = orders[i].Total;
}
var stream = workbook.ToStream();
string filename = $"OrderReport-{DateTime.Now:yyyyMMdd}.xlsx";
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename);
}
}
Imports IronXL
Imports Microsoft.AspNetCore.Mvc
Public Class DataExportController
Inherits Controller
' Export database records to an xlsx file for download
Public Function ExportDatabaseReport() As IActionResult
' Simulate a database query returning order records
Dim orders = New() {
New With {.OrderId = 1001, .Customer = "Acme Corp", .Total = 4500.0D},
New With {.OrderId = 1002, .Customer = "Globex Inc", .Total = 12300.5D},
New With {.OrderId = 1003, .Customer = "Initech LLC", .Total = 890.75D}
}
Dim workbook = WorkBook.Create(ExcelFileFormat.XLSX)
Dim worksheet = workbook.CreateWorkSheet("Orders")
' Write headers
worksheet("A1").Value = "Order ID"
worksheet("B1").Value = "Customer"
worksheet("C1").Value = "Total"
' Write each row of data from the query results
For i As Integer = 0 To orders.Length - 1
Dim row As Integer = i + 2
worksheet($"A{row}").Value = orders(i).OrderId
worksheet($"B{row}").Value = orders(i).Customer
worksheet($"C{row}").Value = orders(i).Total
Next
Dim stream = workbook.ToStream()
Dim filename As String = $"OrderReport-{DateTime.Now:yyyyMMdd}.xlsx"
Return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename)
End Function
End Class
Resultado

En producción, el array de órdenes vendría de una consulta Entity Framework Core, una llamada Dapper o cualquier capa de acceso a datos. El bucle itera por los resultados y escribe cada valor en las celdas correctas. El nombre del archivo incluye la fecha, detalle pequeño que evita confusiones cuando los usuarios descargan ficheros de declaraciones AEAT varias veces. En contextos TicketBAI (Bizkaia, Gipuzkoa, Araba), este mismo patrón se emplea para generar el XLSX de registros de facturación que se adjuntan al sistema de verificación Basque.
IronXL gestiona internamente toda la construcción del fichero XLSX válido. Para escenarios más complejos con fórmulas, estilos o libros con varias hojas (por ejemplo, una hoja por cada operador del modelo 347), el mismo enfoque escala limpiamente.
¿Cómo descargar datos de declaraciones AEAT en CSV en lugar de XLSX?
A veces un fichero CSV ligero es la mejor opción, especialmente cuando los datos deben alimentar otros sistemas, bases de datos o herramientas que no necesitan las funcionalidades ricas de Excel. En el contexto del SII, algunos sistemas de terceros aceptan CSV para los registros de VeriFactu (Real Decreto-Ley 15/2025) antes de su transformación a XML. IronXL hace trivial el cambio entre formatos.
using IronXL;
using Microsoft.AspNetCore.Mvc;
public class CsvController : Controller
{
// Export data as a CSV file download
public IActionResult ExportCsv()
{
var workbook = WorkBook.Create(ExcelFileFormat.XLSX);
var worksheet = workbook.CreateWorkSheet("Products");
worksheet["A1"].Value = "SKU";
worksheet["B1"].Value = "Product";
worksheet["C1"].Value = "Price";
worksheet["A2"].Value = "WDG-001";
worksheet["B2"].Value = "Widget Pro";
worksheet["C2"].Value = 29.99;
// Convert to CSV stream instead of XLSX
var stream = workbook.ToCsvStream();
string filename = "Products.csv";
return File(stream, "text/csv", filename);
}
}
using IronXL;
using Microsoft.AspNetCore.Mvc;
public class CsvController : Controller
{
// Export data as a CSV file download
public IActionResult ExportCsv()
{
var workbook = WorkBook.Create(ExcelFileFormat.XLSX);
var worksheet = workbook.CreateWorkSheet("Products");
worksheet["A1"].Value = "SKU";
worksheet["B1"].Value = "Product";
worksheet["C1"].Value = "Price";
worksheet["A2"].Value = "WDG-001";
worksheet["B2"].Value = "Widget Pro";
worksheet["C2"].Value = 29.99;
// Convert to CSV stream instead of XLSX
var stream = workbook.ToCsvStream();
string filename = "Products.csv";
return File(stream, "text/csv", filename);
}
}
Imports IronXL
Imports Microsoft.AspNetCore.Mvc
Public Class CsvController
Inherits Controller
' Export data as a CSV file download
Public Function ExportCsv() As IActionResult
Dim workbook = WorkBook.Create(ExcelFileFormat.XLSX)
Dim worksheet = workbook.CreateWorkSheet("Products")
worksheet("A1").Value = "SKU"
worksheet("B1").Value = "Product"
worksheet("C1").Value = "Price"
worksheet("A2").Value = "WDG-001"
worksheet("B2").Value = "Widget Pro"
worksheet("C2").Value = 29.99
' Convert to CSV stream instead of XLSX
Dim stream = workbook.ToCsvStream()
Dim filename As String = "Products.csv"
Return File(stream, "text/csv", filename)
End Function
End Class
Fichero CSV de salida

El método ToCsvStream exporta la primera hoja como un MemoryStream en formato CSV. Los ficheros CSV funcionan bien para el intercambio de datos, pero eliminan cualquier formato, fórmulas y estructura de varias hojas. Cuando se necesitan esas funcionalidades —por ejemplo, el fichero XLSX del modelo 390 con formato de celdas y fórmulas de totales— use ToStream() o ToXlsxStream(). Para una visión más profunda de las conversiones de formato, consulte la guía de conversión de hojas de cálculo.
¿Cómo re-exportar una subida de archivo en un formato diferente?
Un escenario habitual en aplicaciones web de gestión fiscal consiste en aceptar ficheros subidos por los usuarios —por ejemplo, el borrador de una declaración en formato XLS— y re-exportarlos en XLSX normalizado antes de enviarlos a la AEAT o almacenarlos según los requisitos de conservación de la LOPDGDD.
public IActionResult ConvertUpload(IFormFile upload)
{
using var memoryStream = new MemoryStream();
upload.CopyTo(memoryStream);
memoryStream.Position = 0;
// Load uploaded Excel file from the stream
var workbook = WorkBook.Load(memoryStream);
// Re-export as XLSX regardless of the original file extension or file type
var stream = workbook.ToXlsxStream();
string filename = Path.GetFileNameWithoutExtension(upload.FileName) + ".xlsx";
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename);
}
public IActionResult ConvertUpload(IFormFile upload)
{
using var memoryStream = new MemoryStream();
upload.CopyTo(memoryStream);
memoryStream.Position = 0;
// Load uploaded Excel file from the stream
var workbook = WorkBook.Load(memoryStream);
// Re-export as XLSX regardless of the original file extension or file type
var stream = workbook.ToXlsxStream();
string filename = Path.GetFileNameWithoutExtension(upload.FileName) + ".xlsx";
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename);
}
Imports Microsoft.AspNetCore.Mvc
Imports System.IO
Public Function ConvertUpload(upload As IFormFile) As IActionResult
Using memoryStream As New MemoryStream()
upload.CopyTo(memoryStream)
memoryStream.Position = 0
' Load uploaded Excel file from the stream
Dim workbook = WorkBook.Load(memoryStream)
' Re-export as XLSX regardless of the original file extension or file type
Dim stream = workbook.ToXlsxStream()
Dim filename As String = Path.GetFileNameWithoutExtension(upload.FileName) & ".xlsx"
Return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename)
End Using
End Function
Resultado: Converting CSV to XLSX Format

El método WorkBook.Load acepta un MemoryStream, por lo que no es necesario guardar los archivos subidos en disco. Detecta automáticamente si el origen es XLS, XLSX o CSV. Desde ahí, ToXlsxStream() genera un MemoryStream limpio en formato XLSX moderno. Este patrón es útil para normalizar envíos de usuarios antes de su procesamiento o almacenamiento, incluyendo la preparación de ficheros de datos Banco de España / CNMV para presentaciones regulatorias en XLSX.
¿Qué formatos de archivo se pueden exportar?
IronXL admite la exportación de datos a múltiples formatos de hoja de cálculo e intercambio de datos. La tabla siguiente resume las opciones de salida disponibles y sus mejores casos de uso, incluyendo los relevantes para declaraciones AEAT.
| Formato| File Extension | Stream Method | Best Use Case | | ---| ---| ---| ---| | XLSX | .xlsx | ToXlsxStream() | Declaraciones AEAT (303, 347, 390), ficheros SII/VeriFactu, presentaciones Banco de España/CNMV | | XLS | .xls | ToXlsStream() | Compatibilidad con sistemas legacy | | CSV | .csv | ToCsvStream() | Intercambio de datos, pipelines de importación, VeriFactu pre-transformación | | XML | .xml | ToXmlStream() | Integración con sistemas empresariales | | JSON | .json | SaveAsJson() | Respuestas de API web e intercambio de datos | | HTML | .html | ExportToHtml() | Visualización de datos de hoja de cálculo en web |
Cada método de flujo devuelve un MemoryStream que se conecta directamente al helper File() de ASP.NET Core. La alternativa de array de bytes (ToByteArray()) también está disponible para escenarios donde un byte[] es más conveniente, como guardar en una base de datos blob o adjuntar a un correo electrónico. Todos estos métodos están disponibles en cuanto se instala el paquete NuGet IronXL. Para una guía completa de cada formato de exportación, consulte la documentación de exportación a Excel.
Al diseñar un sistema de cumplimiento fiscal para la AEAT —ya sea para descargar ficheros XLSX del modelo 303 para asesores fiscales, exportar CSV para ingenieros de datos, o generar XML para integración empresarial— IronXL mantiene el código limpio y la salida fiable en aplicaciones .NET Core y .NET Framework sin Office. ¿Listo para implementar exportaciones de hojas de cálculo en tu próximo proyecto? Obtén una licencia de prueba gratuita o explora las opciones de licencia para comenzar hoy. Para instrucciones de configuración paso a paso, consulte la guía de inicio de IronXL.
Preguntas Frecuentes
¿Cómo descargar un fichero XLSX del modelo 303 de la AEAT desde ASP.NET sin Office?
Use IronXL para crear el libro XLSX con los datos del modelo 303, llame a workbook.ToStream() para obtener un MemoryStream y devuélvalo con return File(stream, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'modelo303.xlsx'). IronXL funciona en Linux/Docker sin Microsoft Office.
¿Puede IronXL generar ficheros XLSX para el SII y VeriFactu desde ASP.NET Core?
Sí. IronXL genera ficheros XLSX de datos SII (Suministro Inmediato de Información) y VeriFactu (Real Decreto-Ley 15/2025) directamente desde controladores ASP.NET Core, ejecutándose en servidores Linux/Docker sin necesidad de Microsoft Office ni Excel Interop.
¿Cómo cumplir con la LOPDGDD al exportar datos de declaraciones AEAT a XLSX?
Al construir el libro XLSX con IronXL, incluya únicamente las columnas de datos necesarias para el trámite fiscal (principio de minimización de la LOPDGDD). No almacene el fichero en disco si no es necesario — use MemoryStream para generar y servir el XLSX directamente en la respuesta HTTP.
¿Cuáles son los beneficios de utilizar IronXL para exportar archivos Excel?
IronXL simplifica la exportación de archivos de Excel mediante la gestión eficaz de MemoryStream y la configuración de encabezados HTTP de disposición de contenido. Funciona en Linux/Docker sin Office, lo que es ideal para servidores de cumplimiento SII/AEAT y entidades supervisadas por el Banco de España/CNMV.
¿A qué formatos puede IronXL exportar datos para declaraciones AEAT?
IronXL puede exportar a XLSX (modelos 303, 347, 390, presentaciones Banco de España/CNMV), CSV (intercambio de datos SII), XML (integración empresarial) y JSON (APIs web). Todos los formatos están disponibles sin Office.
¿Se puede usar IronXL para exportar datos de TicketBAI desde controladores MVC?
Sí. El mismo patrón de controlador MVC con IronXL que genera XLSX para la AEAT funciona para los registros de facturación TicketBAI (Bizkaia, Gipuzkoa, Araba). Construya el libro con los campos requeridos y devuelva el MemoryStream al navegador o al sistema de verificación Basque.



