Persisting Excel Edits in OnlyOffice via Automated Callback Processing

Containerized deployment

docker run -i -t -d -p 80:80 --name onlyoffice onlyoffice/documentserver

Verify the service by accessing the server address in a browser.

Locking specific cells

  1. Open the Excel workbook, select all cells (Ctrl+A) and open the Format Cells dialog (Ctrl+1). On the Protection tab, uncheck the Locked option.
  2. Individually select the cells that should remain protected and mark them as locked via the same dialog.
  3. Go to Review > Protect Sheet. In the permissions list, keep Select unlocked cells checked, and clear the check for Select locked cells. Apply the protection.

Back‑end project setup Download the .NET example from the OnlyOffice website (Communiyt Edition) and unpack it. Inside settings.config, adjust the storage location and your own server’s base URL.

Enabling manual save callback Locate DocEditor.aspx.cs and set the customization flag so that the editor triggers a callback when the user clicks the Save button:

editorConfig.customization.forcesave = true;

Calback endpoint The editor posts status updates to the URL defined by CallBackUrl in DocEditor.aspx. The request is handled by WebEditor.ashx.cs. A manual save produces a MustForceSave state, which invokes the Track method. From the incoming JSON we extract the download URL of the latest file version and pass it to our processing logic.

Extracting cell values The callback handler downloads the file content and uses EPPlus to read specific cells:

private void ProcessSavedFile(string downloadUrl, HttpContext context)
{
    var fileBytes = new WebClient().DownloadData(downloadUrl);
    using (var stream = new MemoryStream(fileBytes))
    using (var package = new ExcelPackage(stream))
    {
        ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
        var ws = package.Workbook.Worksheets[0];

        int trackLength  = Convert.ToInt32(ws.Cells[2, 6].Value);
        int stealCount   = Convert.ToInt32(ws.Cells[5, 6].Value);
        decimal minSpan  = Math.Round(Convert.ToDecimal(ws.Cells[6, 6].Value), 1);
        decimal platLen  = Math.Round(Convert.ToDecimal(ws.Cells[7, 6].Value), 1);
        string remarks   = ws.Cells[4, 7].Value?.ToString() ?? "";

        SaveToDatabase(trackLength, stealCount, minSpan, platLen);
        SaveWordReport(remarks, context);
    }
}

Persisting to Oracle Parameterised queries replace the previous string concatenation to avoid SQL injection and simplify maintenance.

private void SaveToDatabase(int trackLength, int stealCount, decimal minSpan, decimal platLen)
{
    using var conn = new OracleConnection("Data Source=127.0.0.1:1521/orcl;User Id=adu;Password=123456;");
    conn.Open();

    using var lookup = conn.CreateCommand();
    lookup.CommandText = "SELECT Id FROM TableData WHERE TrackLength = :track";
    lookup.Parameters.Add(new OracleParameter("track", trackLength));
    var existingId = lookup.ExecuteScalar() as string;

    if (!string.IsNullOrEmpty(existingId))
    {
        using var upd = conn.CreateCommand();
        upd.CommandText = "UPDATE TableData SET StealCount=:steal, MinSpan=:span, PlatLength=:plat WHERE Id=:id";
        upd.Parameters.Add(new OracleParameter("steal", stealCount));
        upd.Parameters.Add(new OracleParameter("span", minSpan));
        upd.Parameters.Add(new OracleParameter("plat", platLen));
        upd.Parameters.Add(new OracleParameter("id", existingId));
        upd.ExecuteNonQuery();
    }
    else
    {
        using var ins = conn.CreateCommand();
        ins.CommandText = "INSERT INTO TableData(Id, TrackLength, StealCount, MinSpan, PlatLength) VALUES(sys_guid(),:track,:steal,:span,:plat)";
        ins.Parameters.Add(new OracleParameter("track", trackLength));
        ins.Parameters.Add(new OracleParameter("steal", stealCount));
        ins.Parameters.Add(new OracleParameter("span", minSpan));
        ins.Parameters.Add(new OracleParameter("plat", platLen));
        ins.ExecuteNonQuery();
    }
}

Generating a Word report The extracted notes are placed into a new Word document and stored on the server.

private void SaveWordReport(string content, HttpContext context)
{
    string fileName = Guid.NewGuid().ToString("N") + ".docx";
    string fullPath = Path.Combine(context.Server.MapPath("~/App_Data"), fileName);

    var document = new XWPFDocument();
    var paragraph = document.CreateParagraph();
    var run = paragraph.CreateRun();
    run.SetText(content);

    using var fs = new FileStream(fullPath, FileMode.CreateNew);
    document.Write(fs);

    // Additional metadata or logging can be performed here.
}

Tags: OnlyOffice Excel callback data persistence EPPlus

Posted on Wed, 13 May 2026 20:21:42 +0000 by lol