如何使用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
相关产品推荐
相关产品推荐

