You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Azure Function更新Blob中的Excel文件数据

使用C# Azure Function(BlobTrigger)更新Azure存储中的Excel文件列数据

前置依赖

先在Function项目中安装两个NuGet包:

  • DocumentFormat.OpenXml(处理Excel文件)
  • Azure.Storage.Blobs(操作Azure Blob存储)

核心实现思路

Blob触发后,将Excel文件下载到本地临时文件(规避流读写限制),通过OpenXML SDK修改指定列数据,再将修改后的文件上传回Blob存储,最后清理临时文件。

完整代码示例

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Linq;

namespace ExcelUpdateFunction
{
    public class UpdateExcelColumnFunction
    {
        private readonly ILogger<UpdateExcelColumnFunction> _logger;

        public UpdateExcelColumnFunction(ILogger<UpdateExcelColumnFunction> logger)
        {
            _logger = logger;
        }

        [Function("UpdateExcelColumn")]
        public async Task Run(
            [BlobTrigger("your-container-name/{name}", Connection = "AzureWebJobsStorage")] Stream blobStream,
            string name,
            BlobClient blobClient)
        {
            _logger.LogInformation($"Processing Excel file: {name}");

            // 1. 创建临时文件存储Excel内容
            string tempFilePath = Path.Combine(Path.GetTempPath(), $"temp_{name}");
            using (var tempFileStream = new FileStream(tempFilePath, FileMode.Create))
            {
                await blobStream.CopyToAsync(tempFileStream);
            }

            try
            {
                // 输入参数示例:可根据需求动态替换(比如从Blob元数据/请求参数读取)
                string targetColumnName = "Status";
                string newValue = "Processed";

                // 2. 打开Excel文件并修改指定列
                using (SpreadsheetDocument document = SpreadsheetDocument.Open(tempFilePath, true))
                {
                    WorkbookPart workbookPart = document.WorkbookPart;
                    WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
                    SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();

                    // 获取表头行,定位目标列索引
                    Row headerRow = sheetData.Elements<Row>().FirstOrDefault();
                    if (headerRow == null)
                    {
                        _logger.LogError("Excel文件无表头行");
                        return;
                    }

                    int targetColumnIndex = -1;
                    foreach (Cell cell in headerRow.Elements<Cell>())
                    {
                        string cellValue = GetCellValue(document, cell);
                        if (cellValue.Equals(targetColumnName, System.StringComparison.OrdinalIgnoreCase))
                        {
                            targetColumnIndex = CellReferenceToColumnIndex(cell.CellReference);
                            break;
                        }
                    }

                    if (targetColumnIndex == -1)
                    {
                        _logger.LogError($"未找到目标列:{targetColumnName}");
                        return;
                    }

                    // 遍历数据行更新目标列
                    foreach (Row row in sheetData.Elements<Row>().Skip(1)) // 跳过表头行
                    {
                        // 获取目标列单元格,不存在则创建
                        Cell targetCell = row.Elements<Cell>()
                            .FirstOrDefault(c => CellReferenceToColumnIndex(c.CellReference) == targetColumnIndex);

                        if (targetCell == null)
                        {
                            targetCell = new Cell
                            {
                                CellReference = ColumnIndexToCellReference(targetColumnIndex, row.RowIndex.Value)
                            };
                            row.Append(targetCell);
                        }

                        // 设置单元格值
                        targetCell.CellValue = new CellValue(newValue);
                        targetCell.DataType = new EnumValue<CellValues>(CellValues.String);
                    }

                    // 保存修改
                    worksheetPart.Worksheet.Save();
                }

                // 3. 将修改后的文件上传回Blob(覆盖原文件)
                using (var updatedFileStream = new FileStream(tempFilePath, FileMode.Open))
                {
                    await blobClient.UploadAsync(updatedFileStream, new BlobUploadOptions
                    {
                        Overwrite = true
                    });
                }

                _logger.LogInformation($"Excel文件 {name} 已成功更新");
            }
            catch (System.Exception ex)
            {
                _logger.LogError(ex, $"更新Excel文件 {name} 时出错");
            }
            finally
            {
                // 4. 清理临时文件
                if (File.Exists(tempFilePath))
                {
                    File.Delete(tempFilePath);
                }
            }
        }

        // 辅助方法:获取单元格文本值
        private string GetCellValue(SpreadsheetDocument document, Cell cell)
        {
            SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
            if (cell.CellValue == null)
            {
                return string.Empty;
            }

            string value = cell.CellValue.InnerXml;
            if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
            {
                return stringTablePart.SharedStringTable.ChildElements[int.Parse(value)].InnerText;
            }
            else
            {
                return value;
            }
        }

        // 辅助方法:单元格引用转列索引(如A1→1)
        private int CellReferenceToColumnIndex(string cellReference)
        {
            int columnIndex = 0;
            foreach (char c in cellReference.Where(char.IsLetter))
            {
                columnIndex = columnIndex * 26 + (c - 'A' + 1);
            }
            return columnIndex;
        }

        // 辅助方法:列索引转单元格引用前缀(如1→A)
        private string ColumnIndexToCellReference(int columnIndex, uint rowIndex)
        {
            string columnReference = string.Empty;
            while (columnIndex > 0)
            {
                int remainder = (columnIndex - 1) % 26;
                columnReference = System.Convert.ToChar('A' + remainder) + columnReference;
                columnIndex = (columnIndex - remainder) / 26;
            }
            return $"{columnReference}{rowIndex}";
        }
    }
}

关键说明

  1. 参数动态化:若需要动态传递目标列名、更新值,可通过以下方式实现:
    • 读取触发Blob的元数据(await blobClient.GetPropertiesAsync()获取Metadata集合)
    • 结合HTTP触发,将参数作为请求体传入(同时绑定Blob输入)
    • 从环境变量读取配置参数
  2. 临时文件处理:使用本地临时文件避免OpenXML SDK对可读写流的限制,处理完成后务必清理
  3. 单元格兼容:区分共享字符串和普通字符串单元格,确保值的读取与设置逻辑正确
  4. 文件覆盖策略:上传时设置Overwrite = true会覆盖原Blob,若需保留原文件,可修改Blob名称后上传

内容的提问来源于stack exchange,提问作者SKO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:40:32