Web开发:ABP框架10——使用数据库存储文件,完成文件的下载和上传

一、简要介绍

  1. 字节数组:字节数组是存储数据的字节序列,常用于二进制数据(如图片、音视频、文档等)的表示。

  2. 文件和字节的关系:文件是由字节构成,字节是文件内容的基本单位。

  3. 文件以字节形式存储在服务器数据库与文件夹的比较

存储方式优点缺点
数据库存储便于管理和检索,数据安全性高可能占用更多存储空间,性能较低
文件夹存储存储方便,易于访问不便于数据管理和安全控制,缺乏统一性

        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;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值