【问题标题】:Instantiating an IFormFile from a physical file从物理文件实例化 IFormFile
【发布时间】:2016-10-04 08:49:57
【问题描述】:

我有一个物理文件,我想将它传递给我的控制器方法,该方法需要 IFormFile 类型(用于单元测试)。

我找不到任何实例化IFormFile 接口的类,这意味着我无法创建一个。

如何将我的物理文件转换为IFormFile

【问题讨论】:

  • 这里有一个github.com/aspnet/HttpAbstractions/blob/…,但它在内部命名空间中,所以你不应该真正使用它。如果这是用于单元测试,您可以模拟接口或实现您自己的用于测试目的的类。

标签: c# asp.net unit-testing asp.net-core


【解决方案1】:

您可以实例化FormFile。这实现了IFormFile

.Net Core 3.1 中的示例(假设“文件”是物理路径和文件):

using var stream = new MemoryStream(File.ReadAllBytes(file).ToArray());
var formFile = new FormFile(stream, 0, stream.Length, "streamFile", file.Split(@"\").Last());

【讨论】:

  • 我们应该使用具有内部命名空间的类吗?
  • 看起来 FormFile 在 .Net Core 2 中是内部的,现在在 .Net Core 3+ 中是公开的。
【解决方案2】:

假设你有一个接受IFormInfo的控制器方法

[HttpPost]
public Task<IActionResult> Upload(IFormFile file) {

    FileDetails fileDetails = null;
    using (var reader = new StreamReader(file.OpenReadStream())) {
        var fileContent = reader.ReadToEnd();
        ContentDispositionHeaderValue parsedContentDisposition = null;
        if (ContentDispositionHeaderValue.TryParse(file.ContentDisposition, out parsedContentDisposition)) {
            fileDetails = new FileDetails {
                Filename = parsedContentDisposition.FileName,
                Content = fileContent
            };
        }
    }

    return Task.FromResult((IActionResult)fileDetails);

}

使用 Moq 等模拟框架。您可以使用物理文件来模拟IFormFile

[TestMethod]
public async Task Controller_Should_Upload_FormFile() {
    // Arrange.
    var fileMock = new Mock<IFormFile>();
    var physicalFile = new FileInfo("filePath");
    var ms = new MemoryStream();
    var writer = new StreamWriter(ms);
    writer.Write(physicalFile.OpenRead());
    writer.Flush();
    ms.Position = 0;
    var fileName = physicalFile.Name;
    //Setup mock file using info from physical file
    fileMock.Setup(_ => _.FileName).Returns(fileName);
    fileMock.Setup(_ => _.Length).Returns(ms.Length);
    fileMock.Setup(_ => _.OpenReadStream()).Returns(ms);
    fileMock.Setup(_ => _.ContentDisposition).Returns(string.Format("inline; filename={0}", fileName));
    //...setup other members as needed.

    var sut = new MyController();
    var file = fileMock.Object;

    // Act.
    var result = await sut.Upload(file);

    //Assert.
    Assert.IsInstanceOfType(result, typeof(IActionResult));
}

mock的排列甚至可以提取到扩展方法中复用。

public static IFormFile AsMockIFormFile(this FileInfo physicalFile) {
    var fileMock = new Mock<IFormFile>();
    var ms = new MemoryStream();
    var writer = new StreamWriter(ms);
    writer.Write(physicalFile.OpenRead());
    writer.Flush();
    ms.Position = 0;
    var fileName = physicalFile.Name;
    //Setup mock file using info from physical file
    fileMock.Setup(_ => _.FileName).Returns(fileName);
    fileMock.Setup(_ => _.Length).Returns(ms.Length);
    fileMock.Setup(m => m.OpenReadStream()).Returns(ms);
    fileMock.Setup(m => m.ContentDisposition).Returns(string.Format("inline; filename={0}", fileName));
    //...setup other members (code removed for brevity)


    return fileMock.Object;
}

和使用一样

var physicalFile = new FileInfo("filePath");
IFormFile formFile = physicalFile.AsMockIFormFile();

【讨论】:

  • 这看起来没问题,但如果你尝试序列化 fileMock.Object 你会得到异常
【解决方案3】:

我尝试了 Nkosi 的答案(使用 .NET Core 3.1),但它只会返回“System.IO.FileStream”作为文件内容!可能是因为它将 FileStream 对象从 physicalFile.OpenRead() 写入输出流,而不是流的实际内容。这个修改后的版本对我有用:

    public static IFormFile AsMockIFormFile(this FileInfo physicalFile)
    {
        var fileMock = new Mock<IFormFile>();
        var ms = new MemoryStream();
        var writer = new StreamWriter(ms);
        using (FileStream fs = physicalFile.OpenRead())
        {
            byte[] b = new byte[1024];
            UTF8Encoding temp = new UTF8Encoding(true);

            while (fs.Read(b, 0, b.Length) > 0)
            {
                writer.WriteLine(temp.GetString(b));
            }
        }
        writer.Flush();
        ms.Position = 0;
        var fileName = physicalFile.Name;
        //Setup mock file using info from physical file
        fileMock.Setup(_ => _.FileName).Returns(fileName);
        fileMock.Setup(_ => _.Length).Returns(ms.Length);
        fileMock.Setup(m => m.OpenReadStream()).Returns(ms);
        fileMock.Setup(m => m.ContentDisposition).Returns(string.Format("inline; filename={0}", fileName));
        //...setup other members as needed

        return fileMock.Object;
    }

【讨论】:

  • 另请注意,在某些情况下,例如在使用 DocumentFormat.OpenXml 包处理文件时,我还必须添加以下行:physicalFile.IsReadOnly = true;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-18
  • 1970-01-01
相关资源
最近更新 更多