一、简要介绍
-
字节数组:字节数组是存储数据的字节序列,常用于二进制数据(如图片、音视频、文档等)的表示。
-
文件和字节的关系:文件是由字节构成,字节是文件内容的基本单位。
-
文件以字节形式存储在服务器数据库与文件夹的比较:
存储方式 | 优点 | 缺点 |
---|---|---|
数据库存储 | 便于管理和检索,数据安全性高 | 可能占用更多存储空间,性能较低 |
文件夹存储 | 存储方便,易于访问 | 不便于数据管理和安全控制,缺乏统一性 |
4.字节数组在数据库的存储方式
- SQL Server:VARBINARY(最大2GB)
- MySQL:BLOB(最大64KB)、MEDIUMBLOB(最大16MB)、LONGBLOB(最大 4GB)
5.字节数组在C#的类型
public byte[] FileData { get; set; } //使用byte[]表达字节数组
二、本文数据准备
1.数据表
建表语法示例(sqlserver)
USE [TestABP]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Files](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FileName] [nvarchar](255) NULL,
[FileData] [varbinary](max) NULL, -- 使用varbinary表达字节数组
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
2.本文实体
本文采用的ORM框架是:freesql
[Table("Files")]
public class Files
{
[FreeSql.DataAnnotations.Column(IsPrimary = true,IsIdentity = true,Name ="Id")]
public int Id { get; set; }
[Column("FileName")]
public string FileName { get; set; }
[Column("FileData")]
public byte[] FileData { get; set; }
}
3.依赖注入
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IFreeSql _freeSql;//需要安装第三方库,仅仅是一个查库示例
三、【Demo】文件下载
1.字节数组(读库)下载 (后端)
该方案是将文件读入内存再返回,文件较大时会占用较多物理内存,较不建议
/// <summary>
/// 下载文件
/// </summary>
/// <returns></returns>
[Route("DownLoadFile")]
[HttpGet]
public async Task<IActionResult> DownLoadFile()
{
var entity = await _freeSql.Select<Files>().Where(x => x.Id == 1).ToOneAsync();
byte[] file = entity?.FileData;
var fileName = entity?.FileName;
if (file != null)
{
var context = _httpContextAccessor.HttpContext;
var response = context.Response;
response.Headers.Add("Content-Disposition", $"attachment;filename*=utf-8''{WebUtility.UrlEncode(fileName)}");
response.ContentType = "application/octet-stream";
await response.Body.WriteAsync(file, 0, file.Length);
return new EmptyResult(); // 明确返回空结果
}
else
{
throw new FileNotFoundException("文件未找到");
}
}
2.文件路径下载 (后端)
该方案是将文件流直接返回,较为推荐
/// <summary>
/// 下载文件
/// </summary>
/// <returns></returns>
[HttpGet("DownLoadFile")]
public IActionResult DownLoadFile()
{
string path = "C:\\Users\\Mike\\Downloads\\data.png";
if (!File.Exists(path))
{
throw new FileNotFoundException("文件未找到");
}
return new FileStreamResult(File.OpenRead(path), "application/octet-stream")
{
FileDownloadName = "测试Demo.png" // 设置下载文件名
};
}
3. 逻辑路径下载(前端+Nginx)
Vue开发03:Vue中的数据交互的基础小结_vue控件-CSDN博客
(访问【十三、通用下载】目录)
四、【Demo】文件上传
/// <summary>
/// 上传文件
/// </summary>
/// <returns></returns>
[Route("UploadFile")]
[HttpPost]
public async Task<string> UploadFile(IFormFile file)
{
try
{
if (file == null || file.Length == 0)
{
throw new FileNotFoundException("未选择文件上传.");
}
// 读取文件的内容
using (var memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
var fileData = memoryStream.ToArray();//获取上传文件(字节数组形式)
// 下载到指定路径
await File.WriteAllBytesAsync($"E:\\Down\\{file.FileName}", fileData);
// 存库
await _freeSql.Insert(new Files
{
FileData = fileData,//字节数组的形式
FileName = file.FileName, // 可以存储文件名或其他相关信息
}).ExecuteAffrowsAsync();
return "文件上传成功!";
}
}
catch (Exception ex)
{
throw new Exception("上传文件时发生异常: " + ex.ToString(), ex);
}
}
前端代码参照写法:
//假设你已经存在一个文件列表了
for (let i = 0; i < this.fileList.length; i++)
{
const file = this.fileList[i]
const formData = new FormData()
formData.append('file', file)
// 使用axios进行上传,记得Import这个包
var response = await axios.post('/api/books/UploadFile', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
五、【Demo】IFormFile读EXCEL
以下代码,实现了IFormFile读取Excel的内容,需要安装第三方库EPPLUS读取Excel。
[HttpPost]
[Route(nameof(GetExcelInputAsync))]
public async Task GetExcelInputAsync(IFormFile file)
{
try
{
string tips = "";
if (file == null || file.Length == 0)
{
tips = "未选择文件";
}
// 检查文件类型 (这里只做了简单的文件扩展名检查)
var fileExtension = Path.GetExtension(file.FileName).ToLower();
if (fileExtension != ".xls" && fileExtension != ".xlsx")
{
tips = "只支持上传 .xls 或 .xlsx 格式的文件";
}
// 使用内存流读取上传的文件
using (var memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
// 使用 EPPlus 库读取 Excel 文件
using (var package = new ExcelPackage(memoryStream))
{
//设置许可
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
// 获取第一个工作表
var worksheet = package.Workbook.Worksheets[0];
var rowCount = worksheet.Dimension.Rows; // 获取总行数
// 遍历每一行,打印数据(假设数据是逗号分隔的)
for (int row = 1; row <= rowCount; row++) // 从第 1 行开始
{
Console.WriteLine($"第{row}行:");
Console.WriteLine($"第1列的值是:{worksheet.Cells[row, 1].Text}");
Console.WriteLine($"第2列的值是:{worksheet.Cells[row, 2].Text}");
Console.WriteLine($"第3列的值是:{worksheet.Cells[row, 3].Text}");
}
}
}
}
catch (Exception ex)
{
throw;
}
}