Control modular (UserControl) que encapsula la integración con OnlyOffice Document Server.
Permite incrustar un editor de documentos en cualquier página .aspx, pasarle un documento de múltiples formas, y obtener el documento editado desde JavaScript para descargarlo, enviarlo o guardarlo en base de datos.
Targets .NET Framework 4.7.2. Los archivos temporales se almacenan en App_Data/uploads.
Controls/
OnlyOfficeEditor.ascx ← UserControl (markup)
OnlyOfficeEditor.ascx.cs ← Code-behind con propiedades y métodos
OnlyOfficeEditor.ascx.designer.cs
Handlers/
OnlyOfficeHandler.ashx ← HTTP Handler para servir archivos y callbacks
OnlyOfficeHandler.ashx.cs
Scripts/
OnlyOfficeEditor.js ← Módulo JS con API pública del cliente
App_Code/
OnlyOfficeJwt.cs ← Generación y validación de tokens JWT (HS256)
Content/
WebEditor.css ← Estilos del editor y componentes visuales
<%@ Register Src="~/Controls/OnlyOfficeEditor.ascx" TagPrefix="oo" TagName="Editor" %><oo:Editor ID="docEditor" runat="server" />// En el evento de un botón, Page_Load, o donde se necesite:
docEditor.SetDocumentFromBytes(fileBytes, "contrato.docx");Eso es todo. El control se encarga de:
- Almacenar el archivo temporalmente en
App_Data/uploads/. - Generar las URLs de descarga y callback para Document Server.
- Firmar la configuración con JWT.
- Renderizar el editor con el documento cargado.
Ideal para archivos que vienen de un FileUpload, de base de datos o de una API externa.
byte[] fileBytes = fuFile.FileBytes; // desde un FileUpload
docEditor.SetDocumentFromBytes(fileBytes, "reporte.docx");docEditor.SetDocumentFromFile(@"C:\documentos\reporte.docx");
// Opcionalmente se puede dar un nombre para mostrar diferente:
docEditor.SetDocumentFromFile(@"C:\documentos\reporte.docx", "Mi Reporte Final.docx");Si ya se tiene el fileId de un archivo subido previamente:
docEditor.SetDocumentFromUpload("a1b2c3d4e5f6...", "reporte.docx");Para escenarios donde el documento se sirve desde otra fuente (CDN, otro servidor, etc.):
docEditor.DocumentUrl = "https://mi-servidor.com/api/documentos/123/download";
docEditor.DocumentName = "contrato.docx";
docEditor.DocumentKey = "clave-unica-por-version";
docEditor.CallbackUrl = "https://mi-servidor.com/api/documentos/123/callback";Nota:
DocumentKeydebe ser única por cada versión del documento. Si el contenido cambia, la clave debe cambiar también para que Document Server no use una versión cacheada.
Se pueden establecer en el markup o en el code-behind antes del render.
| Propiedad | Tipo | Default | Descripción |
|---|---|---|---|
Mode |
string |
"edit" |
Modo del editor: "edit" o "view". |
Lang |
string |
"es" |
Idioma de la interfaz del editor. |
EditorHeight |
string |
"520px" |
Altura mínima CSS del contenedor. |
OnlyOfficeApiUrl |
string |
(URL del Document Server) | URL completa al api.js de OnlyOffice. |
JwtSecret |
string |
"secreto_personalizado" |
Secreto JWT compartido con Document Server. |
PublicBaseUrl |
string |
"http://192.168.10.34:2355" |
URL base pública de la app (como la ve Document Server). |
UserId |
string |
"1" |
ID del usuario para la sesión del editor. |
UserDisplayName |
string |
"Usuario" |
Nombre del usuario en el editor. |
<oo:Editor ID="docEditor" runat="server"
Mode="edit"
Lang="es"
EditorHeight="700px"
UserId="42"
UserDisplayName="Ana García" />protected void Page_Load(object sender, EventArgs e)
{
docEditor.Mode = "view"; // Solo lectura
docEditor.Lang = "en";
docEditor.EditorHeight = "800px";
}| Propiedad | Tipo | Descripción |
|---|---|---|
EditorContainerId |
string |
ID del DOM del contenedor del editor (único por instancia). Se usa en las llamadas a la API JS. |
HasDocument |
bool |
true si el control tiene un documento válido configurado. |
ConfigJson |
string |
JSON de configuración firmado con JWT (se genera automáticamente en PreRender). |
Todas las funciones del módulo OnlyOfficeEditorModule reciben el containerId como primer parámetro. En el markup se obtiene así:
var containerId = '<%= docEditor.EditorContainerId %>';Solicita a Document Server que genere la URL de descarga del documento editado.
OnlyOfficeEditorModule.getEditedDocumentUrl(containerId)
.then(function(url) {
console.log('URL del documento editado:', url);
// Enviar la URL al servidor, guardar en BD, etc.
})
.catch(function(err) {
console.error('Error:', err);
});Descarga el documento editado directamente en el navegador.
OnlyOfficeEditorModule.downloadDocument(containerId)
.then(function(url) {
console.log('Descarga iniciada desde:', url);
});Obtiene el documento como un Blob de JavaScript. Útil para subirlo a otro servidor o procesarlo en el cliente.
Requisito: Document Server debe tener CORS habilitado para el origen de esta aplicación.
OnlyOfficeEditorModule.getEditedDocumentBlob(containerId)
.then(function(blob) {
// Ejemplo: subir el blob a un API
var fd = new FormData();
fd.append('archivo', blob, 'documento.docx');
return fetch('/api/documentos/guardar', {
method: 'POST',
body: fd
});
})
.then(function(response) {
console.log('Guardado exitosamente');
});Retorna la instancia nativa de DocsAPI.DocEditor para acceso directo al API de OnlyOffice.
var editor = OnlyOfficeEditorModule.getEditor(containerId);
if (editor) {
editor.downloadAs();
}Destruye la instancia del editor y libera recursos del DOM.
OnlyOfficeEditorModule.destroy(containerId);Muestra u oculta el overlay de "Procesando…" sobre el editor.
OnlyOfficeEditorModule.setBusy(containerId, true); // Mostrar
OnlyOfficeEditorModule.setBusy(containerId, false); // OcultarInicializa manualmente el editor (normalmente el control lo invoca automáticamente). Acepta callbacks opcionales:
OnlyOfficeEditorModule.init('mi-editor', configJson, {
onReady: function() { console.log('Editor listo'); },
onDocumentReady: function() { console.log('Documento cargado'); },
onError: function(err) { console.error('Error:', err); },
applyTheme: true // inyecta tema visual al iframe (default: true)
});<%@ Register Src="~/Controls/OnlyOfficeEditor.ascx" TagPrefix="oo" TagName="Editor" %>
<asp:FileUpload ID="fuFile" runat="server" />
<asp:Button ID="btnUpload" runat="server" Text="Abrir" OnClick="btnUpload_Click" />
<oo:Editor ID="docEditor" runat="server" />
<button type="button" onclick="descargar();">Descargar editado</button>
<script>
function descargar() {
var id = '<%= docEditor.EditorContainerId %>';
OnlyOfficeEditorModule.downloadDocument(id);
}
</script>protected void btnUpload_Click(object sender, EventArgs e)
{
if (fuFile.HasFile)
docEditor.SetDocumentFromBytes(fuFile.FileBytes, fuFile.FileName);
}protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
byte[] contenido = MiRepositorio.ObtenerDocumento(idDocumento);
docEditor.SetDocumentFromBytes(contenido, "expediente.docx");
}
}function guardarEnBD() {
var id = '<%= docEditor.EditorContainerId %>';
OnlyOfficeEditorModule.getEditedDocumentBlob(id)
.then(function(blob) {
var fd = new FormData();
fd.append('archivo', blob, 'expediente.docx');
fd.append('idDocumento', '<%= idDocumento %>');
return fetch('/api/documentos/actualizar', {
method: 'POST',
body: fd
});
})
.then(function() { alert('Guardado en base de datos'); });
}<oo:Editor ID="vistaPrevia" runat="server" Mode="view" EditorHeight="400px" />vistaPrevia.SetDocumentFromFile(@"C:\plantillas\formato.docx");<oo:Editor ID="editorOriginal" runat="server" Mode="view" />
<oo:Editor ID="editorCopia" runat="server" Mode="edit" />editorOriginal.SetDocumentFromBytes(bytesOriginal, "original.docx");
editorCopia.SetDocumentFromBytes(bytesCopia, "copia.docx");Cada instancia genera un EditorContainerId único, por lo que no hay conflictos.
El handler desacopla la lógica de servir archivos y recibir callbacks de cualquier página específica.
| Endpoint | Método | Descripción |
|---|---|---|
?action=download&fileId=xxx |
GET | Sirve el archivo de App_Data/uploads/ para Document Server. |
?action=callback&fileId=xxx |
POST | Recibe notificaciones de guardado de Document Server. Responde {"error":0}. |
El control configura estas URLs automáticamente al usar SetDocumentFromBytes, SetDocumentFromFile o SetDocumentFromUpload.
| Qué configurar | Dónde | Descripción |
|---|---|---|
| URL del Document Server | Propiedad OnlyOfficeApiUrl del control |
URL completa al api.js del Document Server. |
| URL base pública | Propiedad PublicBaseUrl del control |
URL por la cual Document Server puede alcanzar esta app (esquema + host + puerto, sin slash final). |
| Secreto JWT | Propiedad JwtSecret del control |
Debe coincidir con el configurado en Document Server. |
- Prerrequisitos: Visual Studio 2022 (o 2019) con .NET Framework 4.7.2 y un OnlyOffice Document Server accesible.
- Restaurar paquetes: Abrir la solución y dejar que NuGet restaure desde
packages.config. - Configurar: Ajustar
OnlyOfficeApiUrl,PublicBaseUrlyJwtSecretsegún el entorno. - Ejecutar: Iniciar con IIS Express. Subir un
.docxo archivo compatible, editarlo y descargarlo.
- ViewState: Las propiedades del documento (
DocumentUrl,DocumentName,DocumentKey,CallbackUrl) se persisten en ViewState, por lo que el editor sobrevive postbacks sin reconfiguración. - JWT: La configuración se firma automáticamente con el secreto definido en
JwtSecret. Asegúrate de que coincida con el configurado en Document Server. - Archivos temporales: Los documentos se almacenan en
App_Data/uploads/. No se eliminan automáticamente; implementa una tarea de limpieza según tu necesidad. - CORS para Blob: Si usas
getEditedDocumentBlob(), Document Server debe permitir peticiones cross-origin desde el dominio de tu aplicación. - Tipos de documento: El control detecta automáticamente el tipo (
word,cell,slide) según la extensión del archivo (.docx,.xlsx,.pptx, etc.). - Secretos hardcodeados: Los valores por defecto de URLs y secretos son para desarrollo local. Muévelos a
Web.configoappSettingsantes de producción.