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

如何使用Razor将BLOB文件内容上传至Azure SQL Server?

CSV文件上传至Azure SQL的问题排查与修复

核心错误点

  • 后端代码错误嵌入前端脚本:你把C#的Upload方法直接写在<script>标签内的Razor代码块中,这是完全错误的逻辑——前端JavaScript无法直接调用后端C#方法,Razor代码仅在服务器渲染页面时执行,不会响应前端按钮点击事件。
  • 数据库连接未初始化:创建SqlConnection后未调用Open()方法,直接执行ExecuteNonQuery()会抛出连接未打开的异常。
  • 表单默认提交行为未阻止:<button>在表单内默认是提交按钮,点击后会刷新页面,导致后续JavaScript逻辑中断。
  • 参数传递不匹配:前端传递的是包含data和fileName的对象,但后端方法需要两个独立参数;且reader.result是ArrayBuffer,无法直接传递给后端,需转换为Base64字符串。
  • 错误日志无效:后端用Console.WriteLine无法在Azure Web App中输出到浏览器控制台,应使用应用日志记录;前端错误需查看浏览器控制台。

修复后的完整实现

1. 前端Razor视图代码

@{
    ViewData["Title"] = "Home Page";
}
@Html.AntiForgeryToken()

<form id="uploadForm">
    <input type="file" id="fileInput" style="display:none;" accept=".csv"/>
    <button type="button" id="selectFileButton">选择CSV文件</button>
    <label id="fileNameLabel" style="margin-left:10px;"></label>
    <button type="button" id="uploadButton">上传至数据库</button>
</form>

@section scripts {
    <script>
        // 绑定文件选择按钮事件
        document.querySelector("#selectFileButton").addEventListener("click", () => {
            document.querySelector("#fileInput").click();
        });

        // 更新选中文件名显示
        document.querySelector("#fileInput").addEventListener("change", function () {
            const file = this.files[0];
            if (file) {
                document.querySelector("#fileNameLabel").innerText = file.name;
            }
        });

        // 上传按钮点击事件
        document.querySelector("#uploadButton").addEventListener("click", async () => {
            const file = document.querySelector("#fileInput").files[0];
            if (!file) {
                alert("请先选择CSV文件");
                return;
            }

            try {
                // 将文件转为Base64字符串(便于HTTP传输)
                const reader = new FileReader();
                const fileContent = await new Promise((resolve) => {
                    reader.onload = (e) => resolve(e.target.result.split(',')[1]);
                    reader.readAsDataURL(file);
                });

                // 构造请求参数
                const params = {
                    fileName: file.name,
                    fileContent: fileContent,
                    customerNumber: "9002",
                    dataType: "TestASP",
                    batchID: 61
                };

                // 调用后端API接口
                const response = await fetch('/Home/UploadFile', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
                    },
                    body: JSON.stringify(params)
                });

                if (response.ok) {
                    alert("上传成功");
                    // 重置表单
                    document.querySelector("#fileNameLabel").innerText = "";
                    document.querySelector("#fileInput").value = "";
                } else {
                    const errorMsg = await response.text();
                    alert(`上传失败: ${errorMsg}`);
                }
            } catch (ex) {
                console.error("上传出错:", ex);
                alert("上传失败,请查看浏览器控制台");
            }
        });
    </script>
}

2. 后端Controller代码(HomeController.cs)

using Microsoft.AspNetCore.Mvc;
using System.Data.SqlClient;
using System.Text;

public class HomeController : Controller
{
    private readonly IConfiguration _config;

    // 通过依赖注入获取配置
    public HomeController(IConfiguration config)
    {
        _config = config;
    }

    [HttpPost]
    public IActionResult UploadFile([FromBody] UploadRequest model)
    {
        try
        {
            // 将Base64字符串转为byte[]
            byte[] csvContent = Convert.FromBase64String(model.FileContent);

            // 从配置文件获取连接字符串(避免硬编码)
            string connStr = _config.GetConnectionString("AzureSqlConnection");

            using (var conn = new SqlConnection(connStr))
            {
                conn.Open(); // 必须打开数据库连接

                // 拆分SQL语句,避免语法错误
                string sql = @"
SET IDENTITY_INSERT BLOBS ON;
INSERT INTO BLOBS (BatchID, CustomerNumber, CSV, DataType) 
VALUES (@BatchID, @CustomerNumber, @CSV, @DataType);
SET IDENTITY_INSERT BLOBS OFF;";

                using (var cmd = new SqlCommand(sql, conn))
                {
                    // 添加参数,防止SQL注入
                    cmd.Parameters.AddWithValue("@CustomerNumber", model.CustomerNumber);
                    cmd.Parameters.AddWithValue("@CSV", csvContent);
                    cmd.Parameters.AddWithValue("@DataType", model.DataType);
                    cmd.Parameters.AddWithValue("@BatchID", model.BatchID);

                    int rows = cmd.ExecuteNonQuery();
                    return rows > 0 ? Ok() : BadRequest("无记录被插入");
                }
            }
        }
        catch (Exception ex)
        {
            // 记录错误到Azure应用日志(可在门户日志流中查看)
            System.Diagnostics.Trace.WriteLine($"上传错误: {ex.Message}\n堆栈信息: {ex.StackTrace}");
            return StatusCode(500, $"服务器错误: {ex.Message}");
        }
    }
}

// 定义接收请求参数的模型
public class UploadRequest
{
    public string FileName { get; set; }
    public string FileContent { get; set; }
    public string CustomerNumber { get; set; }
    public string DataType { get; set; }
    public int BatchID { get; set; }
}

3. 配置连接字符串(appsettings.json)

将Azure SQL连接字符串存入配置文件,避免硬编码:

{
  "ConnectionStrings": {
    "AzureSqlConnection": "你的Azure SQL Server连接字符串"
  }
}

额外注意事项

  • CSRF防护:视图中添加了@Html.AntiForgeryToken(),前端请求携带该Token防止跨站请求伪造。
  • 日志查看:在Azure Portal的App Service中,进入“监控 > 日志流”可查看后端Trace日志,方便排查错误。
  • 大文件限制:若需上传大文件,需在Program.cs中调整请求大小限制:
    builder.Services.Configure<Microsoft.AspNetCore.Http.Features.FormOptions>(options =>
    {
        options.MultipartBodyLengthLimit = 52428800; // 50MB,按需调整
    });
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 13:01:12