Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit 4a51f5a

Browse files
committed
[Fixes #429] FileResult
1) Implemented FilePathResult to efficiently return files from disk. 2) Implemented FileStreamResult to return content from a stream. 3) Implemented FileContentResult to return content from a byte array.
1 parent 1c8582d commit 4a51f5a

File tree

24 files changed

+1498
-2
lines changed

24 files changed

+1498
-2
lines changed

Mvc.sln

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "UrlHelperWebSite", "test\We
8282
EndProject
8383
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ApiExplorerWebSite", "test\WebSites\ApiExplorerWebSite\ApiExplorerWebSite.kproj", "{61061528-071E-424E-965A-07BCC2F02672}"
8484
EndProject
85+
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "FilesWebSite", "test\WebSites\FilesWebSite\FilesWebSite.kproj", "{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}"
86+
EndProject
8587
Global
8688
GlobalSection(SolutionConfigurationPlatforms) = preSolution
8789
Debug|Any CPU = Debug|Any CPU
@@ -412,6 +414,16 @@ Global
412414
{A192E504-2881-41DC-90D1-B7F1DD1134E8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
413415
{A192E504-2881-41DC-90D1-B7F1DD1134E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
414416
{A192E504-2881-41DC-90D1-B7F1DD1134E8}.Release|x86.ActiveCfg = Release|Any CPU
417+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
418+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
419+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
420+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
421+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Debug|x86.ActiveCfg = Debug|Any CPU
422+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
423+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Release|Any CPU.Build.0 = Release|Any CPU
424+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
425+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
426+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}.Release|x86.ActiveCfg = Release|Any CPU
415427
{61061528-071E-424E-965A-07BCC2F02672}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
416428
{61061528-071E-424E-965A-07BCC2F02672}.Debug|Any CPU.Build.0 = Debug|Any CPU
417429
{61061528-071E-424E-965A-07BCC2F02672}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -460,6 +472,7 @@ Global
460472
{1976AC4A-FEA4-4587-A158-D9F79736D2B6} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
461473
{96107AC0-18E2-474D-BAB4-2FFF2185FBCD} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
462474
{A192E504-2881-41DC-90D1-B7F1DD1134E8} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
475+
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
463476
{61061528-071E-424E-965A-07BCC2F02672} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
464477
EndGlobalSection
465478
EndGlobal

samples/MvcSample.Web/HomeController.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ public ActionResult NotFound()
3434
return HttpNotFound();
3535
}
3636

37+
public ActionResult SendFileFromDisk()
38+
{
39+
return File("sample.txt", "text/plain");
40+
}
41+
42+
public ActionResult SendFileFromDiskWithName()
43+
{
44+
return File("sample.txt", "text/plain", "sample-file.txt");
45+
}
46+
3747
public bool IsDefaultNameSpace()
3848
{
3949
var namespaceToken = ActionContext.RouteData.DataTokens["NameSpace"] as string;

samples/MvcSample.Web/sample.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This is a sample file returned from a controller
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNet.Http;
8+
9+
namespace Microsoft.AspNet.Mvc
10+
{
11+
/// <summary>
12+
/// Represents an <see cref="ActionResult"/> that when executed will
13+
/// write a binary file to the response.
14+
/// </summary>
15+
public class FileContentResult : FileResult
16+
{
17+
/// <summary>
18+
/// Creates a new <see cref="FileContentResult"/> instance with
19+
/// the provided <paramref name="fileContents"/> and the
20+
/// provided <paramref name="contentType"/>.
21+
/// </summary>
22+
/// <param name="fileContents">The bytes that represent the file contents.</param>
23+
/// <param name="contentType">The Content-Type header of the response.</param>
24+
public FileContentResult([NotNull] byte[] fileContents, string contentType)
25+
: base(contentType)
26+
{
27+
FileContents = fileContents;
28+
}
29+
30+
/// <summary>
31+
/// Gets the file contents.
32+
/// </summary>
33+
public byte[] FileContents { get; private set; }
34+
35+
/// <inheritdoc />
36+
protected override Task WriteFileAsync(HttpResponse response, CancellationToken cancellation)
37+
{
38+
return response.Body.WriteAsync(FileContents, 0, FileContents.Length, cancellation);
39+
}
40+
}
41+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.IO;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.AspNet.Http;
9+
using Microsoft.AspNet.HttpFeature;
10+
using Microsoft.AspNet.Mvc.Core;
11+
12+
namespace Microsoft.AspNet.Mvc
13+
{
14+
/// <summary>
15+
/// Represents an <see cref="ActionResult"/> that when executed will
16+
/// write a file from disk to the response using mechanisms provided
17+
/// by the host.
18+
/// </summary>
19+
public class FilePathResult : FileResult
20+
{
21+
private const int DefaultBufferSize = 0x1000;
22+
23+
/// <summary>
24+
/// Creates a new <see cref="FilePathResult"/> instance with
25+
/// the provided <paramref name="fileName"/> and the
26+
/// provided <paramref name="contentType"/>.
27+
/// </summary>
28+
/// <param name="fileName">The path to the file. The path must be an absolute
29+
/// path. Relative and virtual paths are not supported.</param>
30+
/// <param name="contentType">The Content-Type header of the response.</param>
31+
public FilePathResult([NotNull] string fileName, [NotNull] string contentType)
32+
: base(contentType)
33+
{
34+
if (!Path.IsPathRooted(fileName))
35+
{
36+
var message = Resources.FormatFileResult_InvalidPathType_RelativeOrVirtualPath(fileName);
37+
throw new ArgumentException(message, "fileName");
38+
}
39+
40+
FileName = fileName;
41+
}
42+
43+
/// <summary>
44+
/// Gets the path to the file that will be sent back as the response.
45+
/// </summary>
46+
public string FileName { get; private set; }
47+
48+
/// <inheritdoc />
49+
protected async override Task WriteFileAsync(HttpResponse response, CancellationToken cancellation)
50+
{
51+
var sendFile = response.HttpContext.GetFeature<IHttpSendFileFeature>();
52+
if (sendFile != null)
53+
{
54+
await sendFile.SendFileAsync(
55+
FileName,
56+
offset: 0,
57+
length: null,
58+
cancellation: cancellation);
59+
}
60+
else
61+
{
62+
await CopyStreamToResponse(FileName, response, cancellation);
63+
}
64+
}
65+
66+
private static async Task CopyStreamToResponse(
67+
string fileName,
68+
HttpResponse response,
69+
CancellationToken cancellation)
70+
{
71+
var fileStream = new FileStream(
72+
fileName, FileMode.Open,
73+
FileAccess.Read,
74+
FileShare.ReadWrite,
75+
DefaultBufferSize,
76+
FileOptions.Asynchronous | FileOptions.SequentialScan);
77+
78+
using (fileStream)
79+
{
80+
await fileStream.CopyToAsync(response.Body, DefaultBufferSize, cancellation);
81+
}
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)