ASP.NET Core Blazor dosyası karşıya yüklemeleri
Uyarı
ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.
Bu makalede, bileşenle birlikte dosyaları Blazor karşıya yükleme işlemi InputFile açıklanmaktadır.
Dosya karşıya yüklemeleri
Uyarı
Kullanıcıların dosyaları karşıya yüklemesine izin verilirken her zaman en iyi güvenlik yöntemlerini izleyin. Daha fazla bilgi için bkz . ASP.NET Core'da dosyaları karşıya yükleme.
InputFile Tarayıcı dosya verilerini .NET koduna okumak için bileşenini kullanın. Bileşen, InputFile tek dosya karşıya yüklemeleri için türünde file
bir HTML <input>
öğesi işler. multiple
Kullanıcının aynı anda birden çok dosya yüklemesine izin vermek için özniteliğini ekleyin.
Bir bileşen veya temel HTML <input type="file">
kullanılırken InputFile dosya seçimi birikmeli olmadığından, var olan bir dosya seçimine dosya ekleyemezsiniz. Bileşen her zaman kullanıcının ilk dosya seçiminin yerini alır, bu nedenle önceki seçimlerden dosya başvuruları kullanılamaz.
() olayı gerçekleştiğinde LoadFiles
aşağıdaki InputFile bileşen yöntemini OnChangechange
yürütür. , InputFileChangeEventArgs seçili dosya listesine ve her dosyayla ilgili ayrıntılara erişim sağlar:
<InputFile OnChange="LoadFiles" multiple />
@code {
private void LoadFiles(InputFileChangeEventArgs e)
{
...
}
}
İşlenen HTML:
<input multiple="" type="file" _bl_2="">
Not
Yukarıdaki örnekte, öğesinin <input>
_bl_2
özniteliği iç işleme için Blazorkullanılır.
Kullanıcı tarafından seçilen bir dosyadaki verileri okumak için, dosyayı arayın IBrowserFile.OpenReadStream ve döndürülen akıştan okuyun. Daha fazla bilgi için Dosya akışları bölümüne bakın.
OpenReadStream bayt cinsinden en büyük boyutu zorlar Stream. Bir dosya veya 500 KB'tan büyük birden çok dosyanın okunması özel durumla sonuçlandı. Bu sınır, geliştiricilerin yanlışlıkla büyük dosyaları belleğe okumasını engeller. maxAllowedSize
parametresiOpenReadStream, gerekirse daha büyük bir boyut belirtmek için kullanılabilir.
Dosyanın baytlarını temsil eden bir Stream öğesine erişmeniz gerekiyorsa kullanın IBrowserFile.OpenReadStream. Gelen dosya akışını bir kerede doğrudan belleğe okumaktan kaçının. Örneğin, dosyanın tüm baytlarını bir MemoryStream içine kopyalamayın veya akışın tamamını aynı anda bayt dizisine okumayın. Bu yaklaşımlar, özellikle sunucu tarafı bileşenler için uygulama performansının düşmesine ve olası Hizmet Reddi (DoS) riskine neden olabilir. Bunun yerine, aşağıdaki yaklaşımlardan birini benimsemeyi göz önünde bulundurun:
- Akışı belleğe okumadan doğrudan disk üzerindeki bir dosyaya kopyalayın. Blazor Sunucuda kod yürüten uygulamaların istemcinin dosya sistemine doğrudan erişemeyeceğini unutmayın.
- dosyaları istemciden doğrudan bir dış hizmete yükleyin. Daha fazla bilgi için Dosyaları dış hizmete yükleme bölümüne bakın.
Aşağıdaki örneklerde, browserFile
karşıya yüklenen dosyayı temsil eder ve uygular IBrowserFile. için IBrowserFile çalışan uygulamalar, bu makalenin devamında dosya yükleme bileşenlerinde gösterilir.
Stream doğrudan tüketiciye sağlanır ve sağlanan yolda dosyayı oluşturan bir FileStream yöntemdir:
Aşağıdaki yaklaşım önerilir çünkü dosyanınkilerawait using FileStream fs = new(path, FileMode.Create);
await browserFile.OpenReadStream().CopyToAsync(fs);
Desteklenir: Aşağıdaki yaklaşım Microsoft Azure Blob Depolama için önerilir çünkü dosyanınkiler Stream doğrudan öğesine UploadBlobAsyncsağlanır:
await blobContainerClient.UploadBlobAsync(
trustedFileName, browserFile.OpenReadStream());
Önerilmez: Dosyanın Stream içeriği bellekte String (reader
) okunduğu için aşağıdaki yaklaşım ÖNERILMEZ:
var reader =
await new StreamReader(browserFile.OpenReadStream()).ReadToEndAsync();
Önerilmez: Aşağıdaki yaklaşım Microsoft Azure Blob Depolama için önerilmez çünkü dosyanın Stream içeriği çağrılmadan UploadBlobAsyncönce bir MemoryStream bellekte (memoryStream
) kopyalanır:
var memoryStream = new MemoryStream();
await browserFile.OpenReadStream().CopyToAsync(memoryStream);
await blobContainerClient.UploadBlobAsync(
trustedFileName, memoryStream));
Görüntü dosyası alan bir bileşen, görüntü uygulamaya akışla aktarilmeden önce tarayıcının JavaScript çalışma zamanı içindeki görüntü verilerini yeniden boyutlandırmak için dosyada kolaylık yöntemini çağırabilir BrowserFileExtensions.RequestImageFileAsync . Arama RequestImageFileAsync için kullanım örnekleri en çok uygulamalar için Blazor WebAssembly uygundur.
Denetimin Ters Çevrilmesi (IoC) kapsayıcısı kullanıcılarını otomatik olarak kullanma
Yerleşik ASP.NET Core bağımlılık ekleme kapsayıcısı yerine Autofac Inversion of Control (IoC) kapsayıcısını kullanıyorsanız, sunucu tarafı bağlantı hattı işleyici hub seçeneklerinde olarak true
ayarlayınDisableImplicitFromServicesParameters. Daha fazla bilgi için bkz . FileUpload: Ayrılan sürede veri alınmadı (dotnet/aspnetcore
#38842).
Dosya boyutu okuma ve karşıya yükleme sınırları
Sunucu tarafı veya istemci tarafı, özellikle bileşen için InputFile dosya okuma veya karşıya yükleme boyutu sınırı yoktur. Ancak istemci tarafı Blazor , verileri JavaScript'ten C# 'ye (2 GB veya cihazın kullanılabilir belleğiyle sınırlı) sıralarken dosyanın baytlarını tek bir JavaScript dizi arabelleğine okur. Büyük dosya yüklemeleri (> 250 MB), bileşeni kullanan InputFile istemci tarafı karşıya yüklemeleri için başarısız olabilir. Daha fazla bilgi için aşağıdaki tartışmalara bakın:
Bileşen için InputFile desteklenen dosya boyutu üst sınırı 2 GB'tır. Ayrıca istemci tarafı Blazor , verileri JavaScript'ten 2 GB veya cihazın kullanılabilir belleğiyle sınırlı olan C# öğesine sıralarken dosyanın baytlarını tek bir JavaScript dizi arabelleğine okur. Büyük dosya yüklemeleri (> 250 MB), bileşeni kullanan InputFile istemci tarafı karşıya yüklemeleri için başarısız olabilir. Daha fazla bilgi için aşağıdaki tartışmalara bakın:
- Blazor Dosya karşıya yüklendiğinde InputFile Bileşeni öbeklemesi işlemelidir (dotnet/runtime #84685)
- http işleyicisi aracılığıyla akış karşıya yükleme isteği (dotnet/runtime #36634)
Bileşeni kullanmaya çalışırken başarısız olan büyük istemci tarafı dosya yüklemeleri için, bileşeni kullanmak InputFile InputFile yerine birden çok HTTP aralığı isteği kullanarak büyük dosyaları özel bir bileşenle öbeklemenizi öneririz.
İstemci tarafı dosya boyutu karşıya yükleme sınırlamasını ele almak için çalışma şu anda .NET 9 (2024 sonu) için zamanlanmıştır.
Örnekler
Aşağıdaki örneklerde bir bileşende birden çok dosya karşıya yükleme gösterilmektedir. InputFileChangeEventArgs.GetMultipleFiles birden çok dosya okumaya izin verir. Kötü amaçlı bir kullanıcının uygulamanın beklediğinden daha fazla sayıda dosya yüklemesini önlemek için en fazla dosya sayısını belirtin. InputFileChangeEventArgs.File , dosya yükleme işlemi birden çok dosyayı desteklemiyorsa ilk ve tek dosyanın okunmasına izin verir.
InputFileChangeEventArgsgenellikle uygulamanın _Imports.razor
dosyasındaki Microsoft.AspNetCore.Components.Forms ad alanlarından biri olan ad alanındadır. Ad alanı dosyada _Imports.razor
mevcut olduğunda, uygulamanın bileşenlerine API üyesi erişimi sağlar.
Dosyadaki _Imports.razor
ad alanları C# dosyalarına (.cs
) uygulanmaz. C# dosyaları, sınıf dosyasının en üstünde açık using
bir yönerge gerektirir:
using Microsoft.AspNetCore.Components.Forms;
Dosya karşıya yükleme bileşenlerini test etme için PowerShell ile her boyutta test dosyası oluşturabilirsiniz:
$out = new-object byte[] {SIZE}; (new-object Random).NextBytes($out); [IO.File]::WriteAllBytes('{PATH}', $out)
Yukarıdaki komutta:
- Yer
{SIZE}
tutucu, dosyanın bayt cinsinden boyutudur (örneğin,2097152
2 MB'lık bir dosya için). - Yer
{PATH}
tutucu, dosya uzantısı olan yol ve dosyadır (örneğin,D:/test_files/testfile2MB.txt
).
Sunucu tarafı dosya yükleme örneği
Aşağıdaki kodu kullanmak için, ortamda çalışan uygulamanın kökünde Development
bir Development/unsafe_uploads
klasör oluşturun.
Örnek, dosyaların kaydedildiği yolun bir parçası olarak uygulamanın ortamını kullandığından, test ve üretimde diğer ortamlar kullanılıyorsa ek klasörler gerekir. Örneğin, ortam için Staging
bir Staging/unsafe_uploads
klasör oluşturun. Ortam için Production
bir Production/unsafe_uploads
klasör oluşturun.
Uyarı
Örnek, dosyaları içeriklerini taramadan kaydeder ve bu makaledeki yönergeler karşıya yüklenen dosyalar için ek güvenlik en iyi yöntemlerini hesaba katmıyor. Hazırlama ve üretim sistemlerinde karşıya yükleme klasöründe yürütme iznini devre dışı bırakın ve karşıya yüklemeden hemen sonra dosyaları virüsten koruma/kötü amaçlı yazılımdan koruma tarayıcı API'siyle tarayın. Daha fazla bilgi için bkz . ASP.NET Core'da dosyaları karşıya yükleme.
FileUpload1.razor
:
@page "/file-upload-1"
@using System
@using System.IO
@using Microsoft.AspNetCore.Hosting
@inject ILogger<FileUpload1> Logger
@inject IWebHostEnvironment Environment
<PageTitle>File Upload 1</PageTitle>
<h1>File Upload Example 1</h1>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Uploading...</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
var trustedFileName = Path.GetRandomFileName();
var path = Path.Combine(Environment.ContentRootPath,
Environment.EnvironmentName, "unsafe_uploads",
trustedFileName);
await using FileStream fs = new(path, FileMode.Create);
await file.OpenReadStream(maxFileSize).CopyToAsync(fs);
loadedFiles.Add(file);
Logger.LogInformation(
"Unsafe Filename: {UnsafeFilename} File saved: {Filename}",
file.Name, trustedFileName);
}
catch (Exception ex)
{
Logger.LogError("File: {Filename} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
@page "/file-upload-1"
@using System
@using System.IO
@using Microsoft.AspNetCore.Hosting
@using Microsoft.Extensions.Logging
@inject ILogger<FileUpload1> Logger
@inject IWebHostEnvironment Environment
<h3>Upload Files</h3>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Uploading...</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
loadedFiles.Add(file);
var trustedFileNameForFileStorage = Path.GetRandomFileName();
var path = Path.Combine(Environment.ContentRootPath,
Environment.EnvironmentName, "unsafe_uploads",
trustedFileNameForFileStorage);
await using FileStream fs = new(path, FileMode.Create);
await file.OpenReadStream(maxFileSize).CopyToAsync(fs);
}
catch (Exception ex)
{
Logger.LogError("File: {Filename} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
@page "/file-upload-1"
@using System
@using System.IO
@using Microsoft.AspNetCore.Hosting
@using Microsoft.Extensions.Logging
@inject ILogger<FileUpload1> Logger
@inject IWebHostEnvironment Environment
<h3>Upload Files</h3>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Uploading...</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
loadedFiles.Add(file);
var trustedFileNameForFileStorage = Path.GetRandomFileName();
var path = Path.Combine(Environment.ContentRootPath,
Environment.EnvironmentName, "unsafe_uploads",
trustedFileNameForFileStorage);
await using FileStream fs = new(path, FileMode.Create);
await file.OpenReadStream(maxFileSize).CopyToAsync(fs);
}
catch (Exception ex)
{
Logger.LogError("File: {Filename} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
@page "/file-upload-1"
@using System
@using System.IO
@using Microsoft.AspNetCore.Hosting
@using Microsoft.Extensions.Logging
@inject ILogger<FileUpload1> Logger
@inject IWebHostEnvironment Environment
<h3>Upload Files</h3>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Uploading...</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
loadedFiles.Add(file);
var trustedFileNameForFileStorage = Path.GetRandomFileName();
var path = Path.Combine(Environment.ContentRootPath,
Environment.EnvironmentName, "unsafe_uploads",
trustedFileNameForFileStorage);
await using FileStream fs = new(path, FileMode.Create);
await file.OpenReadStream(maxFileSize).CopyToAsync(fs);
}
catch (Exception ex)
{
Logger.LogError("File: {Filename} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
İstemci tarafı dosya yükleme örneği
Aşağıdaki örnek dosya baytlarını işler ve uygulama dışındaki bir hedefe dosya göndermez. Sunucuya veya hizmete dosya gönderen bir bileşen örneği Razor için aşağıdaki bölümlere bakın:
Bileşen, Interactive WebAssembly işleme modunun (InteractiveWebAssembly
) bir üst bileşenden devralındığını veya uygulamaya genel olarak uygulandığını varsayar.
@page "/file-upload-1"
@inject ILogger<FileUpload1> Logger
<PageTitle>File Upload 1</PageTitle>
<h1>File Upload Example 1</h1>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Uploading...</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private void LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
loadedFiles.Add(file);
}
catch (Exception ex)
{
Logger.LogError("File: {FileName} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
@page "/file-upload-1"
@using Microsoft.Extensions.Logging
@inject ILogger<FileUpload1> Logger
<h3>Upload Files</h3>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Uploading...</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private void LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
loadedFiles.Add(file);
}
catch (Exception ex)
{
Logger.LogError("File: {FileName} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
@page "/file-upload-1"
@using Microsoft.Extensions.Logging
@inject ILogger<FileUpload1> Logger
<h3>Upload Files</h3>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Uploading...</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private void LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
loadedFiles.Add(file);
}
catch (Exception ex)
{
Logger.LogError("File: {Filename} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
@page "/file-upload-1"
@using Microsoft.Extensions.Logging
@inject ILogger<FileUpload1> Logger
<h3>Upload Files</h3>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Uploading...</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private void LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
loadedFiles.Add(file);
}
catch (Exception ex)
{
Logger.LogError("File: {Filename} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
IBrowserFiletarayıcı tarafından sunulan meta verileri özellik olarak döndürür. Ön doğrulama için bu meta verileri kullanın.
Önceki özelliklerin değerlerine, özellikle de Name kullanıcı arabiriminde görüntülenme özelliğine asla güvenmeyin. Kullanıcı tarafından sağlanan tüm verileri uygulama, sunucu ve ağ için önemli bir güvenlik riski olarak değerlendirin. Daha fazla bilgi için bkz . ASP.NET Core'da dosyaları karşıya yükleme.
Sunucu tarafı işleme ile dosyaları sunucuya yükleme
Bu bölüm, s veya Blazor Server uygulamalardaki Blazor Web AppEtkileşimli Sunucu bileşenleri için geçerlidir.
Aşağıdaki örnekte, bir sunucu tarafı uygulamasından arka uç web API'sinin denetleyicisine ayrı bir uygulamada(muhtemelen ayrı bir sunucuda) dosya yükleme işlemi gösterilmektedir.
Sunucu tarafı uygulamasının dosyasında, uygulamanın Program
örnek oluşturmasına HttpClient izin veren ve ilgili hizmetleri ekleyinIHttpClientFactory:
builder.Services.AddHttpClient();
Daha fazla bilgi için, bkz. ASP.NET Core'da IHttpClientFactory kullanarak HTTP isteği yapma.
Bu bölümdeki örnekler için:
- Web API'si URL'de çalışır:
https://localhost:5001
- Sunucu tarafı uygulaması URL'de çalışır:
https://localhost:5003
Test için, önceki URL'ler projelerin Properties/launchSettings.json
dosyalarında yapılandırılır.
Aşağıdaki UploadResult
sınıf, karşıya yüklenen bir dosyanın sonucunu korur. Bir dosya sunucuya yüklenemediğinde, kullanıcıya görüntülenmesi için içinde ErrorCode
bir hata kodu döndürülür. Her dosya için sunucuda güvenli bir dosya adı oluşturulur ve görüntülenmek üzere istemciye StoredFileName
döndürülür. Dosyalar, içinde güvenli olmayan/güvenilmeyen dosya adı FileName
kullanılarak istemci ile sunucu arasında anahtarlanır.
UploadResult.cs
:
public class UploadResult
{
public bool Uploaded { get; set; }
public string? FileName { get; set; }
public string? StoredFileName { get; set; }
public int ErrorCode { get; set; }
}
Üretim uygulamaları için en iyi güvenlik uygulaması, istemcilere uygulama, sunucu veya ağ hakkındaki hassas bilgileri ortaya çıkarabilecek hata iletileri göndermekten kaçınmaktır. Ayrıntılı hata iletileri sağlamak, kötü amaçlı bir kullanıcının uygulama, sunucu veya ağ üzerindeki saldırıları geliştirmelerine yardımcı olabilir. Bu bölümdeki örnek kod, yalnızca sunucu tarafı hatası oluşursa bileşen istemci tarafı tarafından görüntülenmesi için bir hata kodu numarası (int
) gönderir. Bir kullanıcı dosya yükleme konusunda yardıma ihtiyaç duyarsa, hatanın tam nedenini bilmeden destek bileti çözümü için destek personeline hata kodunu sağlar.
Aşağıdaki LazyBrowserFileStream
sınıf, akışın ilk baytları istenmeden hemen önce lazily çağrıları OpenReadStream özel bir akış türü tanımlar. Akış, .NET'te okunmaya başlayana kadar tarayıcıdan sunucuya iletilmez.
LazyBrowserFileStream.cs
:
using Microsoft.AspNetCore.Components.Forms;
using System.Diagnostics.CodeAnalysis;
namespace BlazorSample;
internal sealed class LazyBrowserFileStream(IBrowserFile file, int maxAllowedSize)
: Stream
{
private readonly IBrowserFile file = file;
private readonly int maxAllowedSize = maxAllowedSize;
private Stream? underlyingStream;
private bool isDisposed;
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => file.Size;
public override long Position
{
get => underlyingStream?.Position ?? 0;
set => throw new NotSupportedException();
}
public override void Flush() => underlyingStream?.Flush();
public override Task<int> ReadAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
{
EnsureStreamIsOpen();
return underlyingStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override ValueTask<int> ReadAsync(Memory<byte> buffer,
CancellationToken cancellationToken = default)
{
EnsureStreamIsOpen();
return underlyingStream.ReadAsync(buffer, cancellationToken);
}
[MemberNotNull(nameof(underlyingStream))]
private void EnsureStreamIsOpen() =>
underlyingStream ??= file.OpenReadStream(maxAllowedSize);
protected override void Dispose(bool disposing)
{
if (isDisposed)
{
return;
}
underlyingStream?.Dispose();
isDisposed = true;
base.Dispose(disposing);
}
public override int Read(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin)
=> throw new NotSupportedException();
public override void SetLength(long value)
=> throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
}
using Microsoft.AspNetCore.Components.Forms;
using System.Diagnostics.CodeAnalysis;
namespace BlazorSample;
internal sealed class LazyBrowserFileStream : Stream
{
private readonly IBrowserFile file;
private readonly int maxAllowedSize;
private Stream? underlyingStream;
private bool isDisposed;
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => file.Size;
public override long Position
{
get => underlyingStream?.Position ?? 0;
set => throw new NotSupportedException();
}
public LazyBrowserFileStream(IBrowserFile file, int maxAllowedSize)
{
this.file = file;
this.maxAllowedSize = maxAllowedSize;
}
public override void Flush()
{
underlyingStream?.Flush();
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
{
EnsureStreamIsOpen();
return underlyingStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override ValueTask<int> ReadAsync(Memory<byte> buffer,
CancellationToken cancellationToken = default)
{
EnsureStreamIsOpen();
return underlyingStream.ReadAsync(buffer, cancellationToken);
}
[MemberNotNull(nameof(underlyingStream))]
private void EnsureStreamIsOpen()
{
underlyingStream ??= file.OpenReadStream(maxAllowedSize);
}
protected override void Dispose(bool disposing)
{
if (isDisposed)
{
return;
}
underlyingStream?.Dispose();
isDisposed = true;
base.Dispose(disposing);
}
public override int Read(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin)
=> throw new NotSupportedException();
public override void SetLength(long value)
=> throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
}
using Microsoft.AspNetCore.Components.Forms;
using System.Diagnostics.CodeAnalysis;
namespace BlazorSample;
internal sealed class LazyBrowserFileStream : Stream
{
private readonly IBrowserFile file;
private readonly int maxAllowedSize;
private Stream? underlyingStream;
private bool isDisposed;
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => file.Size;
public override long Position
{
get => underlyingStream?.Position ?? 0;
set => throw new NotSupportedException();
}
public LazyBrowserFileStream(IBrowserFile file, int maxAllowedSize)
{
this.file = file;
this.maxAllowedSize = maxAllowedSize;
}
public override void Flush()
{
underlyingStream?.Flush();
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
{
EnsureStreamIsOpen();
return underlyingStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override ValueTask<int> ReadAsync(Memory<byte> buffer,
CancellationToken cancellationToken = default)
{
EnsureStreamIsOpen();
return underlyingStream.ReadAsync(buffer, cancellationToken);
}
[MemberNotNull(nameof(underlyingStream))]
private void EnsureStreamIsOpen()
{
underlyingStream ??= file.OpenReadStream(maxAllowedSize);
}
protected override void Dispose(bool disposing)
{
if (isDisposed)
{
return;
}
underlyingStream?.Dispose();
isDisposed = true;
base.Dispose(disposing);
}
public override int Read(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin)
=> throw new NotSupportedException();
public override void SetLength(long value)
=> throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
}
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Forms;
namespace BlazorSample;
internal sealed class LazyBrowserFileStream : Stream
{
private readonly IBrowserFile file;
private readonly int maxAllowedSize;
private Stream? underlyingStream;
private bool isDisposed;
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => file.Size;
public override long Position
{
get => underlyingStream?.Position ?? 0;
set => throw new NotSupportedException();
}
public LazyBrowserFileStream(IBrowserFile file, int maxAllowedSize)
{
this.file = file;
this.maxAllowedSize = maxAllowedSize;
}
public override void Flush()
{
underlyingStream?.Flush();
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
{
EnsureStreamIsOpen();
return underlyingStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override ValueTask<int> ReadAsync(Memory<byte> buffer,
CancellationToken cancellationToken = default)
{
EnsureStreamIsOpen();
return underlyingStream.ReadAsync(buffer, cancellationToken);
}
[MemberNotNull(nameof(underlyingStream))]
private void EnsureStreamIsOpen()
{
underlyingStream ??= file.OpenReadStream(maxAllowedSize);
}
protected override void Dispose(bool disposing)
{
if (isDisposed)
{
return;
}
underlyingStream?.Dispose();
isDisposed = true;
base.Dispose(disposing);
}
public override int Read(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin)
=> throw new NotSupportedException();
public override void SetLength(long value)
=> throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
}
Aşağıdaki FileUpload2
bileşen:
- Kullanıcıların istemciden dosya yüklemesine izin verir.
- kullanıcı arabiriminde istemci tarafından sağlanan güvenilmeyen/güvenli olmayan dosya adını görüntüler. Güvenilmeyen/güvenli olmayan dosya adı, kullanıcı arabiriminde güvenli görüntü için tarafından Razor otomatik olarak HTML ile kodlanır.
Uyarı
İstemciler tarafından sağlanan dosya adlara güvenmeyin:
- Dosyayı bir dosya sistemine veya hizmete kaydetme.
- Dosya adlarını otomatik olarak veya geliştirici kodu aracılığıyla kodlamamış OLAN UI'lerde görüntüleyin.
Bir sunucuya dosya yüklerken dikkat edilmesi gereken güvenlik konuları hakkında daha fazla bilgi için bkz . ASP.NET Core'da dosyaları karşıya yükleme.
FileUpload2.razor
:
@page "/file-upload-2"
@using System.Net.Http.Headers
@using System.Text.Json
@inject IHttpClientFactory ClientFactory
@inject ILogger<FileUpload2> Logger
<PageTitle>File Upload 2</PageTitle>
<h1>File Upload Example 2</h1>
<p>
This example requires a backend server API to function. For more information,
see the <em>Upload files to a server</em> section
of the <em>ASP.NET Core Blazor file uploads</em> article.
</p>
<p>
<label>
Upload up to @maxAllowedFiles files:
<InputFile OnChange="OnInputFileChange" multiple />
</label>
</p>
@if (files.Any())
{
<div class="card">
<div class="card-body">
<ul>
@foreach (var file in files)
{
<li>
File: @file.Name
<br>
@if (FileUpload(uploadResults, file.Name, Logger,
out var result))
{
<span>
Stored File Name: @result.StoredFileName
</span>
}
else
{
<span>
There was an error uploading the file
(Error: @result.ErrorCode).
</span>
}
</li>
}
</ul>
</div>
</div>
}
@code {
private List<File> files = new();
private List<UploadResult> uploadResults = new();
private int maxAllowedFiles = 3;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
shouldRender = false;
int maxFileSize = 1024 * 15;
var upload = false;
using var content = new MultipartFormDataContent();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
if (uploadResults.SingleOrDefault(
f => f.FileName == file.Name) is null)
{
try
{
files.Add(new() { Name = file.Name });
var stream = new LazyBrowserFileStream(file, maxFileSize);
var fileContent = new StreamContent(stream);
fileContent.Headers.ContentType =
new MediaTypeHeaderValue(file.ContentType);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
upload = true;
}
catch (Exception ex)
{
Logger.LogInformation(
"{FileName} not uploaded (Err: 6): {Message}",
file.Name, ex.Message);
uploadResults.Add(
new()
{
FileName = file.Name,
ErrorCode = 6,
Uploaded = false
});
}
}
}
if (upload)
{
var client = ClientFactory.CreateClient();
var response =
await client.PostAsync("https://localhost:5001/Filesave",
content);
if (response.IsSuccessStatusCode)
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
using var responseStream =
await response.Content.ReadAsStreamAsync();
var newUploadResults = await JsonSerializer
.DeserializeAsync<IList<UploadResult>>(responseStream, options);
if (newUploadResults is not null)
{
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
}
}
shouldRender = true;
}
private static bool FileUpload(IList<UploadResult> uploadResults,
string? fileName, ILogger<FileUpload2> logger, out UploadResult result)
{
result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new();
if (!result.Uploaded)
{
logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName);
result.ErrorCode = 5;
}
return result.Uploaded;
}
private class File
{
public string? Name { get; set; }
}
}
@page "/file-upload-2"
@using System.Net.Http.Headers
@using System.Text.Json
@inject IHttpClientFactory ClientFactory
@inject ILogger<FileUpload2> Logger
<h1>File Upload Example 2</h1>
<p>
This example requires a backend server API to function. For more information,
see the <em>Upload files to a server</em> section
of the <em>ASP.NET Core Blazor file uploads</em> article.
</p>
<p>
<label>
Upload up to @maxAllowedFiles files:
<InputFile OnChange="OnInputFileChange" multiple />
</label>
</p>
@if (files.Count > 0)
{
<div class="card">
<div class="card-body">
<ul>
@foreach (var file in files)
{
<li>
File: @file.Name
<br>
@if (FileUpload(uploadResults, file.Name, Logger,
out var result))
{
<span>
Stored File Name: @result.StoredFileName
</span>
}
else
{
<span>
There was an error uploading the file
(Error: @result.ErrorCode).
</span>
}
</li>
}
</ul>
</div>
</div>
}
@code {
private List<File> files = new();
private List<UploadResult> uploadResults = new();
private int maxAllowedFiles = 3;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
shouldRender = false;
int maxFileSize = 1024 * 15;
var upload = false;
using var content = new MultipartFormDataContent();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
if (uploadResults.SingleOrDefault(
f => f.FileName == file.Name) is null)
{
try
{
files.Add(new() { Name = file.Name });
var stream = new LazyBrowserFileStream(file, maxFileSize);
var fileContent = new StreamContent(stream);
fileContent.Headers.ContentType =
new MediaTypeHeaderValue(file.ContentType);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
upload = true;
}
catch (Exception ex)
{
Logger.LogInformation(
"{FileName} not uploaded (Err: 6): {Message}",
file.Name, ex.Message);
uploadResults.Add(
new()
{
FileName = file.Name,
ErrorCode = 6,
Uploaded = false
});
}
}
}
if (upload)
{
var client = ClientFactory.CreateClient();
var response =
await client.PostAsync("https://localhost:5001/Filesave",
content);
if (response.IsSuccessStatusCode)
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
using var responseStream =
await response.Content.ReadAsStreamAsync();
var newUploadResults = await JsonSerializer
.DeserializeAsync<IList<UploadResult>>(responseStream, options);
if (newUploadResults is not null)
{
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
}
}
shouldRender = true;
}
private static bool FileUpload(IList<UploadResult> uploadResults,
string? fileName, ILogger<FileUpload2> logger, out UploadResult result)
{
result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new();
if (!result.Uploaded)
{
logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName);
result.ErrorCode = 5;
}
return result.Uploaded;
}
private class File
{
public string? Name { get; set; }
}
}
@page "/file-upload-2"
@using System.Net.Http.Headers
@using System.Text.Json
@inject IHttpClientFactory ClientFactory
@inject ILogger<FileUpload2> Logger
<h1>File Upload Example 2</h1>
<p>
This example requires a backend server API to function. For more information,
see the <em>Upload files to a server</em> section
of the <em>ASP.NET Core Blazor file uploads</em> article.
</p>
<p>
<label>
Upload up to @maxAllowedFiles files:
<InputFile OnChange="OnInputFileChange" multiple />
</label>
</p>
@if (files.Count > 0)
{
<div class="card">
<div class="card-body">
<ul>
@foreach (var file in files)
{
<li>
File: @file.Name
<br>
@if (FileUpload(uploadResults, file.Name, Logger,
out var result))
{
<span>
Stored File Name: @result.StoredFileName
</span>
}
else
{
<span>
There was an error uploading the file
(Error: @result.ErrorCode).
</span>
}
</li>
}
</ul>
</div>
</div>
}
@code {
private List<File> files = new();
private List<UploadResult> uploadResults = new();
private int maxAllowedFiles = 3;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
shouldRender = false;
int maxFileSize = 1024 * 15;
var upload = false;
using var content = new MultipartFormDataContent();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
if (uploadResults.SingleOrDefault(
f => f.FileName == file.Name) is null)
{
try
{
files.Add(new() { Name = file.Name });
var stream = new LazyBrowserFileStream(file, maxFileSize);
var fileContent = new StreamContent(stream);
fileContent.Headers.ContentType =
new MediaTypeHeaderValue(file.ContentType);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
upload = true;
}
catch (Exception ex)
{
Logger.LogInformation(
"{FileName} not uploaded (Err: 6): {Message}",
file.Name, ex.Message);
uploadResults.Add(
new()
{
FileName = file.Name,
ErrorCode = 6,
Uploaded = false
});
}
}
}
if (upload)
{
var client = ClientFactory.CreateClient();
var response =
await client.PostAsync("https://localhost:5001/Filesave",
content);
if (response.IsSuccessStatusCode)
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
using var responseStream =
await response.Content.ReadAsStreamAsync();
var newUploadResults = await JsonSerializer
.DeserializeAsync<IList<UploadResult>>(responseStream, options);
if (newUploadResults is not null)
{
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
}
}
shouldRender = true;
}
private static bool FileUpload(IList<UploadResult> uploadResults,
string? fileName, ILogger<FileUpload2> logger, out UploadResult result)
{
result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new();
if (!result.Uploaded)
{
logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName);
result.ErrorCode = 5;
}
return result.Uploaded;
}
private class File
{
public string? Name { get; set; }
}
}
@page "/file-upload-2"
@using System.Net.Http.Headers
@using System.Text.Json
@using Microsoft.Extensions.Logging
@inject IHttpClientFactory ClientFactory
@inject ILogger<FileUpload2> Logger
<h1>File Upload Example 2</h1>
<p>
This example requires a backend server API to function. For more information,
see the <em>Upload files to a server</em> section
of the <em>ASP.NET Core Blazor file uploads</em> article.
</p>
<p>
<label>
Upload up to @maxAllowedFiles files:
<InputFile OnChange="OnInputFileChange" multiple />
</label>
</p>
@if (files.Count > 0)
{
<div class="card">
<div class="card-body">
<ul>
@foreach (var file in files)
{
<li>
File: @file.Name
<br>
@if (FileUpload(uploadResults, file.Name, Logger,
out var result))
{
<span>
Stored File Name: @result.StoredFileName
</span>
}
else
{
<span>
There was an error uploading the file
(Error: @result.ErrorCode).
</span>
}
</li>
}
</ul>
</div>
</div>
}
@code {
private List<File> files = new();
private List<UploadResult> uploadResults = new();
private int maxAllowedFiles = 3;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
shouldRender = false;
int maxFileSize = 1024 * 15;
var upload = false;
using var content = new MultipartFormDataContent();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
if (uploadResults.SingleOrDefault(
f => f.FileName == file.Name) is null)
{
try
{
files.Add(new() { Name = file.Name });
var stream = new LazyBrowserFileStream(file, maxFileSize);
var fileContent = new StreamContent(stream);
fileContent.Headers.ContentType =
new MediaTypeHeaderValue(file.ContentType);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
upload = true;
}
catch (Exception ex)
{
Logger.LogInformation(
"{FileName} not uploaded (Err: 6): {Message}",
file.Name, ex.Message);
uploadResults.Add(
new()
{
FileName = file.Name,
ErrorCode = 6,
Uploaded = false
});
}
}
}
if (upload)
{
var client = ClientFactory.CreateClient();
var response =
await client.PostAsync("https://localhost:5001/Filesave",
content);
if (response.IsSuccessStatusCode)
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
using var responseStream =
await response.Content.ReadAsStreamAsync();
var newUploadResults = await JsonSerializer
.DeserializeAsync<IList<UploadResult>>(responseStream, options);
if (newUploadResults is not null)
{
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
}
}
shouldRender = true;
}
private static bool FileUpload(IList<UploadResult> uploadResults,
string fileName, ILogger<FileUpload2> logger, out UploadResult result)
{
result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new();
if (!result.Uploaded)
{
logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName);
result.ErrorCode = 5;
}
return result.Uploaded;
}
private class File
{
public string Name { get; set; }
}
}
Bileşen, dosyayı tek seferde tek bir dosyaya yüklerse veya bileşen yalnızca etkileşimli istemci tarafı işlemeyi (CSR, ) benimsiyorsa, InteractiveWebAssembly
bileşen öğesinin LazyBrowserFileStream
kullanılmasını önleyebilir ve kullanabilir Stream. Aşağıda, bileşene yönelik değişiklikler gösterilmektedir FileUpload2
:
- var stream = new LazyBrowserFileStream(file, maxFileSize);
- var fileContent = new StreamContent(stream);
+ var fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
Kullanılmadığından sınıfını LazyBrowserFileStream
()LazyBrowserFileStream.cs
kaldırın.
Bileşen limit dosyası tek seferde tek bir dosyaya yüklenirse, bileşen ve kullanmaktan LazyBrowserFileStream
Streamkaçınabilir. Aşağıda, bileşene yönelik değişiklikler gösterilmektedir FileUpload2
:
- var stream = new LazyBrowserFileStream(file, maxFileSize);
- var fileContent = new StreamContent(stream);
+ var fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
Kullanılmadığından sınıfını LazyBrowserFileStream
()LazyBrowserFileStream.cs
kaldırın.
Web API projesindeki aşağıdaki denetleyici, istemciden karşıya yüklenen dosyaları kaydeder.
Önemli
Bu bölümdeki denetleyici, uygulamadan ayrı bir web API'sinde Blazor kullanılmak üzere tasarlanmıştır. Dosya yükleme kullanıcılarının kimliği doğrulanırsa web API'sinin Siteler Arası İstek Sahteciliği (XSRF/CSRF) saldırılarını azaltması gerekir.
Not
.NET 6'da ASP.NET Core'daki Minimum API'ler için özniteliğine sahip [FromForm]
form değerlerini bağlama yerel olarak desteklenmez. Bu nedenle, aşağıdaki Filesave
denetleyici örneği Minimum API'leri kullanacak şekilde dönüştürülemez. Minimum API'lere sahip form değerlerinden bağlama desteği ,NET 7 veya sonraki sürümlerde ASP.NET Core'da kullanılabilir.
Aşağıdaki kodu kullanmak için, ortamda çalışan uygulamanın web API'sinin projesinin kökünde Development
bir Development/unsafe_uploads
klasör oluşturun.
Örnek, dosyaların kaydedildiği yolun bir parçası olarak uygulamanın ortamını kullandığından, test ve üretimde diğer ortamlar kullanılıyorsa ek klasörler gerekir. Örneğin, ortam için Staging
bir Staging/unsafe_uploads
klasör oluşturun. Ortam için Production
bir Production/unsafe_uploads
klasör oluşturun.
Uyarı
Örnek, dosyaları içeriklerini taramadan kaydeder ve bu makaledeki yönergeler karşıya yüklenen dosyalar için ek güvenlik en iyi yöntemlerini hesaba katmıyor. Hazırlama ve üretim sistemlerinde karşıya yükleme klasöründe yürütme iznini devre dışı bırakın ve karşıya yüklemeden hemen sonra dosyaları virüsten koruma/kötü amaçlı yazılımdan koruma tarayıcı API'siyle tarayın. Daha fazla bilgi için bkz . ASP.NET Core'da dosyaları karşıya yükleme.
Controllers/FilesaveController.cs
:
using System.Net;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("[controller]")]
public class FilesaveController(
IHostEnvironment env, ILogger<FilesaveController> logger)
: ControllerBase
{
[HttpPost]
public async Task<ActionResult<IList<UploadResult>>> PostFile(
[FromForm] IEnumerable<IFormFile> files)
{
var maxAllowedFiles = 3;
long maxFileSize = 1024 * 15;
var filesProcessed = 0;
var resourcePath = new Uri($"{Request.Scheme}://{Request.Host}/");
List<UploadResult> uploadResults = [];
foreach (var file in files)
{
var uploadResult = new UploadResult();
string trustedFileNameForFileStorage;
var untrustedFileName = file.FileName;
uploadResult.FileName = untrustedFileName;
var trustedFileNameForDisplay =
WebUtility.HtmlEncode(untrustedFileName);
if (filesProcessed < maxAllowedFiles)
{
if (file.Length == 0)
{
logger.LogInformation("{FileName} length is 0 (Err: 1)",
trustedFileNameForDisplay);
uploadResult.ErrorCode = 1;
}
else if (file.Length > maxFileSize)
{
logger.LogInformation("{FileName} of {Length} bytes is " +
"larger than the limit of {Limit} bytes (Err: 2)",
trustedFileNameForDisplay, file.Length, maxFileSize);
uploadResult.ErrorCode = 2;
}
else
{
try
{
trustedFileNameForFileStorage = Path.GetRandomFileName();
var path = Path.Combine(env.ContentRootPath,
env.EnvironmentName, "unsafe_uploads",
trustedFileNameForFileStorage);
await using FileStream fs = new(path, FileMode.Create);
await file.CopyToAsync(fs);
logger.LogInformation("{FileName} saved at {Path}",
trustedFileNameForDisplay, path);
uploadResult.Uploaded = true;
uploadResult.StoredFileName = trustedFileNameForFileStorage;
}
catch (IOException ex)
{
logger.LogError("{FileName} error on upload (Err: 3): {Message}",
trustedFileNameForDisplay, ex.Message);
uploadResult.ErrorCode = 3;
}
}
filesProcessed++;
}
else
{
logger.LogInformation("{FileName} not uploaded because the " +
"request exceeded the allowed {Count} of files (Err: 4)",
trustedFileNameForDisplay, maxAllowedFiles);
uploadResult.ErrorCode = 4;
}
uploadResults.Add(uploadResult);
}
return new CreatedResult(resourcePath, uploadResults);
}
}
Yukarıdaki kodda, GetRandomFileName güvenli bir dosya adı oluşturmak için çağrılır. Tarayıcı tarafından sağlanan dosya adına asla güvenmeyin; bir siber saldırı uzmanı, var olan bir dosyanın üzerine yazan veya uygulamanın dışına yazmaya çalışan bir yol gönderen mevcut bir dosya adını seçebilir.
Sunucu uygulamasının denetleyici hizmetlerini ve eşleme denetleyicisi uç noktalarını kaydetmesi gerekir. Daha fazla bilgi için bkz . ASP.NET Core'da denetleyici eylemlerine yönlendirme.
İstemci tarafı işleme (CSR) ile bir sunucuya dosya yükleme
Bu bölüm, s veya Blazor WebAssembly uygulamalardaki Blazor Web Appistemci tarafı işlenmiş (CSR) bileşenleri için geçerlidir.
Aşağıdaki örnek, CSR veya uygulamadaki bir bileşeni benimseyen bir bileşenden ayrı bir uygulamadaki Blazor Web App (büyük olasılıkla ayrı bir sunucuda) arka uç web API'sinin denetleyicisine Blazor WebAssembly dosya yükleme işlemini gösterir.
Aşağıdaki UploadResult
sınıf, karşıya yüklenen bir dosyanın sonucunu korur. Bir dosya sunucuya yüklenemediğinde, kullanıcıya görüntülenmesi için içinde ErrorCode
bir hata kodu döndürülür. Her dosya için sunucuda güvenli bir dosya adı oluşturulur ve görüntülenmek üzere istemciye StoredFileName
döndürülür. Dosyalar, içinde güvenli olmayan/güvenilmeyen dosya adı FileName
kullanılarak istemci ile sunucu arasında anahtarlanır.
UploadResult.cs
:
public class UploadResult
{
public bool Uploaded { get; set; }
public string? FileName { get; set; }
public string? StoredFileName { get; set; }
public int ErrorCode { get; set; }
}
Not
Önceki UploadResult
sınıf istemci ve sunucu tabanlı projeler arasında paylaşılabilir. İstemci ve sunucu projeleri sınıfı paylaştığında, paylaşılan proje için her projenin _Imports.razor
dosyasına bir içeri aktarma ekleyin. Örneğin:
@using BlazorSample.Shared
Aşağıdaki FileUpload2
bileşen:
- Kullanıcıların istemciden dosya yüklemesine izin verir.
- kullanıcı arabiriminde istemci tarafından sağlanan güvenilmeyen/güvenli olmayan dosya adını görüntüler. Güvenilmeyen/güvenli olmayan dosya adı, kullanıcı arabiriminde güvenli görüntü için tarafından Razor otomatik olarak HTML ile kodlanır.
Üretim uygulamaları için en iyi güvenlik uygulaması, istemcilere uygulama, sunucu veya ağ hakkındaki hassas bilgileri ortaya çıkarabilecek hata iletileri göndermekten kaçınmaktır. Ayrıntılı hata iletileri sağlamak, kötü amaçlı bir kullanıcının uygulama, sunucu veya ağ üzerindeki saldırıları geliştirmelerine yardımcı olabilir. Bu bölümdeki örnek kod, yalnızca sunucu tarafı hatası oluşursa bileşen istemci tarafı tarafından görüntülenmesi için bir hata kodu numarası (int
) gönderir. Bir kullanıcı dosya yükleme konusunda yardıma ihtiyaç duyarsa, hatanın tam nedenini bilmeden destek bileti çözümü için destek personeline hata kodunu sağlar.
Uyarı
İstemciler tarafından sağlanan dosya adlara güvenmeyin:
- Dosyayı bir dosya sistemine veya hizmete kaydetme.
- Dosya adlarını otomatik olarak veya geliştirici kodu aracılığıyla kodlamamış OLAN UI'lerde görüntüleyin.
Bir sunucuya dosya yüklerken dikkat edilmesi gereken güvenlik konuları hakkında daha fazla bilgi için bkz . ASP.NET Core'da dosyaları karşıya yükleme.
Ana projedeBlazor Web App, projenin Program
dosyasına ve ilgili hizmetleri ekleyinIHttpClientFactory:
builder.Services.AddHttpClient();
HttpClient
İstemci tarafı bileşeni sunucuda önceden depolandığından, hizmetler ana projeye eklenmelidir. Aşağıdaki bileşen için ön kayıt özelliğini devre dışı bırakırsanız, hizmetleri ana uygulamada sağlamanız HttpClient
gerekmez ve önceki satırı ana projeye eklemeniz gerekmez.
ASP.NET Core uygulamasına hizmet ekleme HttpClient
hakkında daha fazla bilgi için bkz . ASP.NET Core'da IHttpClientFactory kullanarak HTTP istekleri oluşturma.
bir istemci projesinin Blazor Web App (.Client
) bir arka uç web API denetleyicisine HTTP POST istekleri için de kaydetmesi HttpClient gerekir. İstemci projesinin Program
dosyasına aşağıdakileri onaylayın veya ekleyin:
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
Yukarıdaki örnek, uygulamanın temel adresini alan ve genellikle ana bilgisayar sayfasındaki etiketin href
değerinden <base>
türetilen temel adresi ()IWebAssemblyHostEnvironment.BaseAddress ile builder.HostEnvironment.BaseAddress
ayarlar. Dış web API'sini çağırıyorsanız URI'yi web API'sinin temel adresine ayarlayın.
içinde aşağıdaki bileşenin Blazor Web Appen üstünde Interactive WebAssembly işleme modu özniteliğini belirtin:
@rendermode InteractiveWebAssembly
FileUpload2.razor
:
@page "/file-upload-2"
@using System.Linq
@using System.Net.Http.Headers
@inject HttpClient Http
@inject ILogger<FileUpload2> Logger
<PageTitle>File Upload 2</PageTitle>
<h1>File Upload Example 2</h1>
<p>
<label>
Upload up to @maxAllowedFiles files:
<InputFile OnChange="OnInputFileChange" multiple />
</label>
</p>
@if (files.Count > 0)
{
<div class="card">
<div class="card-body">
<ul>
@foreach (var file in files)
{
<li>
File: @file.Name
<br>
@if (FileUpload(uploadResults, file.Name, Logger,
out var result))
{
<span>
Stored File Name: @result.StoredFileName
</span>
}
else
{
<span>
There was an error uploading the file
(Error: @result.ErrorCode).
</span>
}
</li>
}
</ul>
</div>
</div>
}
@code {
private List<File> files = new();
private List<UploadResult> uploadResults = new();
private int maxAllowedFiles = 3;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
shouldRender = false;
long maxFileSize = 1024 * 15;
var upload = false;
using var content = new MultipartFormDataContent();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
if (uploadResults.SingleOrDefault(
f => f.FileName == file.Name) is null)
{
try
{
files.Add(new() { Name = file.Name });
var fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
fileContent.Headers.ContentType =
new MediaTypeHeaderValue(file.ContentType);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
upload = true;
}
catch (Exception ex)
{
Logger.LogInformation(
"{FileName} not uploaded (Err: 6): {Message}",
file.Name, ex.Message);
uploadResults.Add(
new()
{
FileName = file.Name,
ErrorCode = 6,
Uploaded = false
});
}
}
}
if (upload)
{
var response = await Http.PostAsync("/Filesave", content);
var newUploadResults = await response.Content
.ReadFromJsonAsync<IList<UploadResult>>();
if (newUploadResults is not null)
{
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
}
shouldRender = true;
}
private static bool FileUpload(IList<UploadResult> uploadResults,
string? fileName, ILogger<FileUpload2> logger, out UploadResult result)
{
result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new();
if (!result.Uploaded)
{
logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName);
result.ErrorCode = 5;
}
return result.Uploaded;
}
private class File
{
public string? Name { get; set; }
}
}
@page "/file-upload-2"
@using System.Linq
@using System.Net.Http.Headers
@using Microsoft.Extensions.Logging
@inject HttpClient Http
@inject ILogger<FileUpload2> Logger
<h1>Upload Files</h1>
<p>
<label>
Upload up to @maxAllowedFiles files:
<InputFile OnChange="OnInputFileChange" multiple />
</label>
</p>
@if (files.Count > 0)
{
<div class="card">
<div class="card-body">
<ul>
@foreach (var file in files)
{
<li>
File: @file.Name
<br>
@if (FileUpload(uploadResults, file.Name, Logger,
out var result))
{
<span>
Stored File Name: @result.StoredFileName
</span>
}
else
{
<span>
There was an error uploading the file
(Error: @result.ErrorCode).
</span>
}
</li>
}
</ul>
</div>
</div>
}
@code {
private List<File> files = new();
private List<UploadResult> uploadResults = new();
private int maxAllowedFiles = 3;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
shouldRender = false;
long maxFileSize = 1024 * 15;
var upload = false;
using var content = new MultipartFormDataContent();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
if (uploadResults.SingleOrDefault(
f => f.FileName == file.Name) is null)
{
try
{
files.Add(new() { Name = file.Name });
var fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
fileContent.Headers.ContentType =
new MediaTypeHeaderValue(file.ContentType);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
upload = true;
}
catch (Exception ex)
{
Logger.LogInformation(
"{FileName} not uploaded (Err: 6): {Message}",
file.Name, ex.Message);
uploadResults.Add(
new()
{
FileName = file.Name,
ErrorCode = 6,
Uploaded = false
});
}
}
}
if (upload)
{
var response = await Http.PostAsync("/Filesave", content);
var newUploadResults = await response.Content
.ReadFromJsonAsync<IList<UploadResult>>();
if (newUploadResults is not null)
{
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
}
shouldRender = true;
}
private static bool FileUpload(IList<UploadResult> uploadResults,
string? fileName, ILogger<FileUpload2> logger, out UploadResult result)
{
result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new();
if (!result.Uploaded)
{
logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName);
result.ErrorCode = 5;
}
return result.Uploaded;
}
private class File
{
public string? Name { get; set; }
}
}
@page "/file-upload-2"
@using System.Linq
@using System.Net.Http.Headers
@using Microsoft.Extensions.Logging
@inject HttpClient Http
@inject ILogger<FileUpload2> Logger
<h1>Upload Files</h1>
<p>
<label>
Upload up to @maxAllowedFiles files:
<InputFile OnChange="OnInputFileChange" multiple />
</label>
</p>
@if (files.Count > 0)
{
<div class="card">
<div class="card-body">
<ul>
@foreach (var file in files)
{
<li>
File: @file.Name
<br>
@if (FileUpload(uploadResults, file.Name, Logger,
out var result))
{
<span>
Stored File Name: @result.StoredFileName
</span>
}
else
{
<span>
There was an error uploading the file
(Error: @result.ErrorCode).
</span>
}
</li>
}
</ul>
</div>
</div>
}
@code {
private List<File> files = new();
private List<UploadResult> uploadResults = new();
private int maxAllowedFiles = 3;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
shouldRender = false;
long maxFileSize = 1024 * 15;
var upload = false;
using var content = new MultipartFormDataContent();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
if (uploadResults.SingleOrDefault(
f => f.FileName == file.Name) is null)
{
try
{
files.Add(new() { Name = file.Name });
var fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
fileContent.Headers.ContentType =
new MediaTypeHeaderValue(file.ContentType);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
upload = true;
}
catch (Exception ex)
{
Logger.LogInformation(
"{FileName} not uploaded (Err: 6): {Message}",
file.Name, ex.Message);
uploadResults.Add(
new()
{
FileName = file.Name,
ErrorCode = 6,
Uploaded = false
});
}
}
}
if (upload)
{
var response = await Http.PostAsync("/Filesave", content);
var newUploadResults = await response.Content
.ReadFromJsonAsync<IList<UploadResult>>();
if (newUploadResults is not null)
{
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
}
shouldRender = true;
}
private static bool FileUpload(IList<UploadResult> uploadResults,
string? fileName, ILogger<FileUpload2> logger, out UploadResult result)
{
result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new();
if (!result.Uploaded)
{
logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName);
result.ErrorCode = 5;
}
return result.Uploaded;
}
private class File
{
public string? Name { get; set; }
}
}
@page "/file-upload-2"
@using System.Linq
@using System.Net.Http.Headers
@using Microsoft.Extensions.Logging
@inject HttpClient Http
@inject ILogger<FileUpload2> Logger
<h1>Upload Files</h1>
<p>
<label>
Upload up to @maxAllowedFiles files:
<InputFile OnChange="OnInputFileChange" multiple />
</label>
</p>
@if (files.Count > 0)
{
<div class="card">
<div class="card-body">
<ul>
@foreach (var file in files)
{
<li>
File: @file.Name
<br>
@if (FileUpload(uploadResults, file.Name, Logger,
out var result))
{
<span>
Stored File Name: @result.StoredFileName
</span>
}
else
{
<span>
There was an error uploading the file
(Error: @result.ErrorCode).
</span>
}
</li>
}
</ul>
</div>
</div>
}
@code {
private List<File> files = new();
private List<UploadResult> uploadResults = new();
private int maxAllowedFiles = 3;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
shouldRender = false;
long maxFileSize = 1024 * 15;
var upload = false;
using var content = new MultipartFormDataContent();
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
if (uploadResults.SingleOrDefault(
f => f.FileName == file.Name) is null)
{
try
{
files.Add(new() { Name = file.Name });
var fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
fileContent.Headers.ContentType =
new MediaTypeHeaderValue(file.ContentType);
content.Add(
content: fileContent,
name: "\"files\"",
fileName: file.Name);
upload = true;
}
catch (Exception ex)
{
Logger.LogInformation(
"{FileName} not uploaded (Err: 6): {Message}",
file.Name, ex.Message);
uploadResults.Add(
new()
{
FileName = file.Name,
ErrorCode = 6,
Uploaded = false
});
}
}
}
if (upload)
{
var response = await Http.PostAsync("/Filesave", content);
var newUploadResults = await response.Content
.ReadFromJsonAsync<IList<UploadResult>>();
uploadResults = uploadResults.Concat(newUploadResults).ToList();
}
shouldRender = true;
}
private static bool FileUpload(IList<UploadResult> uploadResults,
string fileName, ILogger<FileUpload2> logger, out UploadResult result)
{
result = uploadResults.SingleOrDefault(f => f.FileName == fileName);
if (result is null)
{
logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName);
result = new();
result.ErrorCode = 5;
}
return result.Uploaded;
}
private class File
{
public string Name { get; set; }
}
}
Sunucu tarafı projesindeki aşağıdaki denetleyici, karşıya yüklenen dosyaları istemciden kaydeder.
Not
.NET 6'da ASP.NET Core'daki Minimum API'ler için özniteliğine sahip [FromForm]
form değerlerini bağlama yerel olarak desteklenmez. Bu nedenle, aşağıdaki Filesave
denetleyici örneği Minimum API'leri kullanacak şekilde dönüştürülemez. Minimum API'lere sahip form değerlerinden bağlama desteği ,NET 7 veya sonraki sürümlerde ASP.NET Core'da kullanılabilir.
Aşağıdaki kodu kullanmak için, ortamda çalışan uygulamanın sunucu tarafı projesinin kökünde Development
bir Development/unsafe_uploads
klasör oluşturun.
Örnek, dosyaların kaydedildiği yolun bir parçası olarak uygulamanın ortamını kullandığından, test ve üretimde diğer ortamlar kullanılıyorsa ek klasörler gerekir. Örneğin, ortam için Staging
bir Staging/unsafe_uploads
klasör oluşturun. Ortam için Production
bir Production/unsafe_uploads
klasör oluşturun.
Uyarı
Örnek, dosyaları içeriklerini taramadan kaydeder ve bu makaledeki yönergeler karşıya yüklenen dosyalar için ek güvenlik en iyi yöntemlerini hesaba katmıyor. Hazırlama ve üretim sistemlerinde karşıya yükleme klasöründe yürütme iznini devre dışı bırakın ve karşıya yüklemeden hemen sonra dosyaları virüsten koruma/kötü amaçlı yazılımdan koruma tarayıcı API'siyle tarayın. Daha fazla bilgi için bkz . ASP.NET Core'da dosyaları karşıya yükleme.
Aşağıdaki örnekte, paylaşılan proje sınıfını sağladığında paylaşılan projenin ad alanını paylaşılan projeyle eşleşecek şekilde güncelleştirin UploadResult
.
Controllers/FilesaveController.cs
:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using BlazorSample.Shared;
[ApiController]
[Route("[controller]")]
public class FilesaveController(
IHostEnvironment env, ILogger<FilesaveController> logger)
: ControllerBase
{
[HttpPost]
public async Task<ActionResult<IList<UploadResult>>> PostFile(
[FromForm] IEnumerable<IFormFile> files)
{
var maxAllowedFiles = 3;
long maxFileSize = 1024 * 15;
var filesProcessed = 0;
var resourcePath = new Uri($"{Request.Scheme}://{Request.Host}/");
List<UploadResult> uploadResults = [];
foreach (var file in files)
{
var uploadResult = new UploadResult();
string trustedFileNameForFileStorage;
var untrustedFileName = file.FileName;
uploadResult.FileName = untrustedFileName;
var trustedFileNameForDisplay =
WebUtility.HtmlEncode(untrustedFileName);
if (filesProcessed < maxAllowedFiles)
{
if (file.Length == 0)
{
logger.LogInformation("{FileName} length is 0 (Err: 1)",
trustedFileNameForDisplay);
uploadResult.ErrorCode = 1;
}
else if (file.Length > maxFileSize)
{
logger.LogInformation("{FileName} of {Length} bytes is " +
"larger than the limit of {Limit} bytes (Err: 2)",
trustedFileNameForDisplay, file.Length, maxFileSize);
uploadResult.ErrorCode = 2;
}
else
{
try
{
trustedFileNameForFileStorage = Path.GetRandomFileName();
var path = Path.Combine(env.ContentRootPath,
env.EnvironmentName, "unsafe_uploads",
trustedFileNameForFileStorage);
await using FileStream fs = new(path, FileMode.Create);
await file.CopyToAsync(fs);
logger.LogInformation("{FileName} saved at {Path}",
trustedFileNameForDisplay, path);
uploadResult.Uploaded = true;
uploadResult.StoredFileName = trustedFileNameForFileStorage;
}
catch (IOException ex)
{
logger.LogError("{FileName} error on upload (Err: 3): {Message}",
trustedFileNameForDisplay, ex.Message);
uploadResult.ErrorCode = 3;
}
}
filesProcessed++;
}
else
{
logger.LogInformation("{FileName} not uploaded because the " +
"request exceeded the allowed {Count} of files (Err: 4)",
trustedFileNameForDisplay, maxAllowedFiles);
uploadResult.ErrorCode = 4;
}
uploadResults.Add(uploadResult);
}
return new CreatedResult(resourcePath, uploadResults);
}
}
Yukarıdaki kodda, GetRandomFileName güvenli bir dosya adı oluşturmak için çağrılır. Tarayıcı tarafından sağlanan dosya adına asla güvenmeyin; bir siber saldırı uzmanı, var olan bir dosyanın üzerine yazan veya uygulamanın dışına yazmaya çalışan bir yol gönderen mevcut bir dosya adını seçebilir.
Sunucu uygulamasının denetleyici hizmetlerini ve eşleme denetleyicisi uç noktalarını kaydetmesi gerekir. Daha fazla bilgi için bkz . ASP.NET Core'da denetleyici eylemlerine yönlendirme.
Dosya yükleme işlemini iptal etme
Bir dosya karşıya yükleme bileşeni, bir kullanıcının veya StreamReader.ReadAsynciçine IBrowserFile.OpenReadStream çağrı yaparken kullanarak CancellationToken bir karşıya yüklemeyi ne zaman iptal ettiğinde algılayabilir.
Bileşen için InputFile
bir CancellationTokenSource oluşturun. Yöntemin OnInputFileChange
başlangıcında, önceki bir karşıya yükleme işleminin devam ediyor olup olmadığını denetleyin.
Karşıya dosya yükleme işlemi sürüyorsa:
- Önceki karşıya yüklemede arayın Cancel .
- Sonraki karşıya yükleme için yeni CancellationTokenSource bir oluşturma ve veya ReadAsync'CancellationTokenSource.Tokenye OpenReadStream geçirme.
Dosyaları sunucu tarafında ilerleme durumuyla karşıya yükleme
Aşağıdaki örnekte, kullanıcıya karşıya yükleme ilerleme durumunun görüntülendiği sunucu tarafı bir uygulamada dosyaların nasıl karşıya yükleneceği gösterilmektedir.
Bir test uygulamasında aşağıdaki örneği kullanmak için:
- Karşıya yüklenen dosyaları ortama kaydetmek için
Development
bir klasör oluşturun:Development/unsafe_uploads
. - En büyük dosya boyutunu (
maxFileSize
aşağıdaki örnekte 15 KB) ve izin verilen en fazla dosya sayısını (maxAllowedFiles
aşağıdaki örnekte 3) yapılandırın. - Devam eden raporlamada daha fazla ayrıntı düzeyi için arabelleği farklı bir değere (aşağıdaki örnekte 10 KB) ayarlayın. Performans ve güvenlik endişeleri nedeniyle 30 KB'tan büyük bir arabellek kullanılması önerilmez.
Uyarı
Örnek, dosyaları içeriklerini taramadan kaydeder ve bu makaledeki yönergeler karşıya yüklenen dosyalar için ek güvenlik en iyi yöntemlerini hesaba katmıyor. Hazırlama ve üretim sistemlerinde karşıya yükleme klasöründe yürütme iznini devre dışı bırakın ve karşıya yüklemeden hemen sonra dosyaları virüsten koruma/kötü amaçlı yazılımdan koruma tarayıcı API'siyle tarayın. Daha fazla bilgi için bkz . ASP.NET Core'da dosyaları karşıya yükleme.
FileUpload3.razor
:
@page "/file-upload-3"
@inject ILogger<FileUpload3> Logger
@inject IWebHostEnvironment Environment
<PageTitle>File Upload 3</PageTitle>
<h1>File Upload Example 3</h1>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Progress: @string.Format("{0:P0}", progressPercent)</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private decimal progressPercent;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
progressPercent = 0;
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
var trustedFileName = Path.GetRandomFileName();
var path = Path.Combine(Environment.ContentRootPath,
Environment.EnvironmentName, "unsafe_uploads", trustedFileName);
await using FileStream writeStream = new(path, FileMode.Create);
using var readStream = file.OpenReadStream(maxFileSize);
var bytesRead = 0;
var totalRead = 0;
var buffer = new byte[1024 * 10];
while ((bytesRead = await readStream.ReadAsync(buffer)) != 0)
{
totalRead += bytesRead;
await writeStream.WriteAsync(buffer, 0, bytesRead);
progressPercent = Decimal.Divide(totalRead, file.Size);
StateHasChanged();
}
loadedFiles.Add(file);
Logger.LogInformation(
"Unsafe Filename: {UnsafeFilename} File saved: {Filename}",
file.Name, trustedFileName);
}
catch (Exception ex)
{
Logger.LogError("File: {FileName} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
@page "/file-upload-3"
@using System
@using System.IO
@using Microsoft.AspNetCore.Hosting
@using Microsoft.Extensions.Logging
@inject ILogger<FileUpload3> Logger
@inject IWebHostEnvironment Environment
<h3>Upload Files</h3>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Progress: @string.Format("{0:P0}", progressPercent)</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private decimal progressPercent;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
progressPercent = 0;
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
var trustedFileName = Path.GetRandomFileName();
var path = Path.Combine(Environment.ContentRootPath,
Environment.EnvironmentName, "unsafe_uploads", trustedFileName);
await using FileStream writeStream = new(path, FileMode.Create);
using var readStream = file.OpenReadStream(maxFileSize);
var bytesRead = 0;
var totalRead = 0;
var buffer = new byte[1024 * 10];
while ((bytesRead = await readStream.ReadAsync(buffer)) != 0)
{
totalRead += bytesRead;
await writeStream.WriteAsync(buffer, 0, bytesRead);
progressPercent = Decimal.Divide(totalRead, file.Size);
StateHasChanged();
}
loadedFiles.Add(file);
}
catch (Exception ex)
{
Logger.LogError("File: {FileName} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
@page "/file-upload-3"
@using System
@using System.IO
@using Microsoft.AspNetCore.Hosting
@using Microsoft.Extensions.Logging
@inject ILogger<FileUpload3> Logger
@inject IWebHostEnvironment Environment
<h3>Upload Files</h3>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Progress: @string.Format("{0:P0}", progressPercent)</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private decimal progressPercent;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
progressPercent = 0;
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
var trustedFileName = Path.GetRandomFileName();
var path = Path.Combine(Environment.ContentRootPath,
Environment.EnvironmentName, "unsafe_uploads", trustedFileName);
await using FileStream writeStream = new(path, FileMode.Create);
using var readStream = file.OpenReadStream(maxFileSize);
var bytesRead = 0;
var totalRead = 0;
var buffer = new byte[1024 * 10];
while ((bytesRead = await readStream.ReadAsync(buffer)) != 0)
{
totalRead += bytesRead;
await writeStream.WriteAsync(buffer, 0, bytesRead);
progressPercent = Decimal.Divide(totalRead, file.Size);
StateHasChanged();
}
loadedFiles.Add(file);
}
catch (Exception ex)
{
Logger.LogError("File: {Filename} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
@page "/file-upload-3"
@using System
@using System.IO
@using Microsoft.AspNetCore.Hosting
@using Microsoft.Extensions.Logging
@inject ILogger<FileUpload3> Logger
@inject IWebHostEnvironment Environment
<h3>Upload Files</h3>
<p>
<label>
Max file size:
<input type="number" @bind="maxFileSize" />
</label>
</p>
<p>
<label>
Max allowed files:
<input type="number" @bind="maxAllowedFiles" />
</label>
</p>
<p>
<label>
Upload up to @maxAllowedFiles of up to @maxFileSize bytes:
<InputFile OnChange="LoadFiles" multiple />
</label>
</p>
@if (isLoading)
{
<p>Progress: @string.Format("{0:P0}", progressPercent)</p>
}
else
{
<ul>
@foreach (var file in loadedFiles)
{
<li>
<ul>
<li>Name: @file.Name</li>
<li>Last modified: @file.LastModified.ToString()</li>
<li>Size (bytes): @file.Size</li>
<li>Content type: @file.ContentType</li>
</ul>
</li>
}
</ul>
}
@code {
private List<IBrowserFile> loadedFiles = new();
private long maxFileSize = 1024 * 15;
private int maxAllowedFiles = 3;
private bool isLoading;
private decimal progressPercent;
private async Task LoadFiles(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
progressPercent = 0;
foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
{
try
{
var trustedFileName = Path.GetRandomFileName();
var path = Path.Combine(Environment.ContentRootPath,
Environment.EnvironmentName, "unsafe_uploads", trustedFileName);
await using FileStream writeStream = new(path, FileMode.Create);
using var readStream = file.OpenReadStream(maxFileSize);
var bytesRead = 0;
var totalRead = 0;
var buffer = new byte[1024 * 10];
while ((bytesRead = await readStream.ReadAsync(buffer)) != 0)
{
totalRead += bytesRead;
await writeStream.WriteAsync(buffer, 0, bytesRead);
progressPercent = Decimal.Divide(totalRead, file.Size);
StateHasChanged();
}
loadedFiles.Add(file);
}
catch (Exception ex)
{
Logger.LogError("File: {Filename} Error: {Error}",
file.Name, ex.Message);
}
}
isLoading = false;
}
}
Daha fazla bilgi için aşağıdaki API kaynaklarına bakın:
- FileStream: Hem zaman uyumlu hem de zaman uyumsuz okuma ve yazma işlemlerini destekleyen bir dosya sağlar Stream .
- FileStream.ReadAsync: Önceki
FileUpload3
bileşen akışı ile ReadAsynczaman uyumsuz olarak okur. Akış ile zaman uyumlu okuma Read , bileşenlerde Razor desteklenmez.
Dosya akışları
Sunucu etkileşimi ile dosya verileri, dosya okundukça sunucudaki .NET koduna bağlantı üzerinden SignalR akışla aktarılır.
RemoteBrowserFileStreamOptions dosya karşıya yükleme özelliklerinin yapılandırılmasına izin verir.
WebAssembly tarafından işlenen bir bileşen için dosya verileri doğrudan tarayıcıdaki .NET koduna akışla aktarılır.
Karşıya resim önizlemesi yükleme
Görüntüleri karşıya yüklemenin görüntü önizlemesi için, bileşen başvurusu ve işleyicisi olan bir OnChange
bileşen ekleyerek InputFile
başlayın:
<InputFile @ref="inputFile" OnChange="ShowPreview" />
Görüntü önizlemesi için yer tutucu işlevi görecek bir öğe başvurusuna sahip bir görüntü öğesi ekleyin:
<img @ref="previewImageElem" />
İlişkili başvuruları ekleyin:
@code {
private InputFile? inputFile;
private ElementReference previewImageElem;
}
JavaScript'te, aşağıdakileri gerçekleştiren HTML input
ve img
öğesiyle adlı bir işlev ekleyin:
- Seçili dosyayı ayıklar.
- ile
createObjectURL
bir nesne URL'si oluşturur. - Görüntü yüklendikten sonra nesne URL'sini
revokeObjectURL
iptal etmek için bir olay dinleyicisi ayarlar, böylece bellek sızdırılamaz. img
Görüntüyü görüntülemek için öğenin kaynağını ayarlar.
window.previewImage = (inputElem, imgElem) => {
const url = URL.createObjectURL(inputElem.files[0]);
imgElem.addEventListener('load', () => URL.revokeObjectURL(url), { once: true });
imgElem.src = url;
}
Son olarak, JavaScript işlevini çağıran işleyiciyi eklemek OnChange
için eklenen IJSRuntime öğesini kullanın:
@inject IJSRuntime JS
...
@code {
...
private async Task ShowPreview() => await JS.InvokeVoidAsync(
"previewImage", inputFile!.Element, previewImageElem);
}
Yukarıdaki örnek, tek bir görüntüyü karşıya yüklemeye yöneliktir. Yaklaşım, görüntüleri destekleyecek multiple
şekilde genişletilebilir.
Aşağıdaki FileUpload4
bileşende tam örnek gösterilmektedir.
FileUpload4.razor
:
@page "/file-upload-4"
@inject IJSRuntime JS
<h1>File Upload Example</h1>
<InputFile @ref="inputFile" OnChange="ShowPreview" />
<img style="max-width:200px;max-height:200px" @ref="previewImageElem" />
@code {
private InputFile? inputFile;
private ElementReference previewImageElem;
private async Task ShowPreview() => await JS.InvokeVoidAsync(
"previewImage", inputFile!.Element, previewImageElem);
}
@page "/file-upload-4"
@inject IJSRuntime JS
<h1>File Upload Example</h1>
<InputFile @ref="inputFile" OnChange="ShowPreview" />
<img style="max-width:200px;max-height:200px" @ref="previewImageElem" />
@code {
private InputFile? inputFile;
private ElementReference previewImageElem;
private async Task ShowPreview() => await JS.InvokeVoidAsync(
"previewImage", inputFile!.Element, previewImageElem);
}
Dosyaları dış hizmete yükleme
İstemciler, dosya yükleme baytlarını işleyen bir uygulama ve karşıya yüklenen dosyaları alan uygulamanın sunucusu yerine dosyaları doğrudan bir dış hizmete yükleyebilir. Uygulama, dış hizmetten gelen dosyaları isteğe bağlı olarak güvenli bir şekilde işleyebilir. Bu yaklaşım, uygulamayı ve sunucusunu kötü amaçlı saldırılara ve olası performans sorunlarına karşı güçlendirmektedir.
Aşağıdaki olası avantajlara sahip Azure Dosyalar, Azure Blob Depolama veya üçüncü taraf hizmet kullanan bir yaklaşımı göz önünde bulundurun:
- Dosyaları istemciden doğrudan JavaScript istemci kitaplığı veya REST API'sine sahip bir dış hizmete yükleyin. Örneğin, Azure aşağıdaki istemci kitaplıklarını ve API'leri sunar:
- Her istemci dosyası yüklemesi için uygulama (sunucu tarafı) tarafından oluşturulan bir kullanıcı temsilcisi paylaşılan erişim imzası (SAS) belirteci ile kullanıcı yüklemelerini yetkileyin. Örneğin Azure aşağıdaki SAS özelliklerini sunar:
- Otomatik yedeklilik ve dosya paylaşımı yedeklemesi sağlayın.
- Karşıya yüklemeleri kotalarla sınırlayın. Azure Blob Depolama kotalarının kapsayıcı düzeyinde değil hesap düzeyinde ayarlandığını unutmayın. Ancak Azure Dosyalar kotaları dosya paylaşımı düzeyindedir ve karşıya yükleme sınırları üzerinde daha iyi denetim sağlayabilir. Daha fazla bilgi için bu listenin önceki bölümlerinde bağlantı verilen Azure belgelerine bakın.
- Sunucu tarafı şifrelemesi (SSE) ile dosyaların güvenliğini sağlama.
Azure Blob Depolama ve Azure Dosyalar hakkında daha fazla bilgi için Azure Depolama belgelerine bakın.
Sunucu tarafı SignalR ileti boyutu sınırı
Dosya yüklemeleri, en büyük SignalR ileti boyutunu aşan dosyalar hakkındaki verileri aldığında, Blazor bunlar başlamadan önce bile başarısız olabilir.
SignalR , alınan her ileti için geçerli olan bir ileti Blazor boyutu sınırı tanımlar ve InputFile bileşen, yapılandırılan sınıra uygun iletilerde dosyaları sunucuya akışla aktarır. Ancak, karşıya yüklenecek dosya kümesini gösteren ilk ileti benzersiz bir tek ileti olarak gönderilir. İlk iletinin boyutu ileti boyutu sınırını aşabilir SignalR . Sorun dosyaların boyutuyla değil, dosya sayısıyla ilgilidir.
Günlüğe kaydedilen hata aşağıdakine benzer:
Hata: Bağlantı 'Hata: Sunucu kapatma sırasında bir hata döndürdü: Bağlantı bir hatayla kapatıldı' hatasıyla kesildi. blazor@.server e.log.js:1
Dosyaları karşıya yüklerken, ilk iletide ileti boyutu sınırına ulaşmak nadirdir. Sınıra ulaşılırsa uygulama daha büyük bir değerle yapılandırılabilir HubOptions.MaximumReceiveMessageSize .
Yapılandırma ve ayarlama hakkında SignalR daha fazla bilgi için bkz. ASP.NET Core BlazorSignalR kılavuzu.MaximumReceiveMessageSize
İstemci hub'ı başına en fazla paralel çağrı ayarı
BlazorMaximumParallelInvocationsPerClient varsayılan değer olan 1 olarak ayarlanır.
Değerin artırılması, işlemlerin oluşturma System.InvalidOperationException: 'Reading is not allowed after reader was completed.'
olasılığının yüksek olmasına CopyTo
neden olur. Daha fazla bilgi için bkz . MaximumParallelInvocationsPerClient > 1, dosya yüklemeyi modda sonlar Blazor Server (dotnet/aspnetcore
#53951).
Sorun giderme
Çağıran IBrowserFile.OpenReadStream satır bir System.TimeoutExceptionoluşturur:
System.TimeoutException: Did not receive any data in the allotted time.
Olası nedenler:
Yerleşik ASP.NET Core bağımlılık ekleme kapsayıcısı yerine Autofac Inversion of Control (IoC) kapsayıcısını kullanma. Sorunu çözmek için sunucu tarafı bağlantı hattı işleyici hub seçeneklerinde olarak ayarlayın DisableImplicitFromServicesParameters
true
. Daha fazla bilgi için bkz . FileUpload: Ayrılan sürede veri alınmadı (dotnet/aspnetcore
#38842).Akış tamamlanmadan okunmuyor. Bu bir çerçeve sorunu değildir. Özel durumu yakalayın ve yerel ortamınızda/ağınızda daha fazla araştırın.
- Tamamlanmak üzere okumadan önce birden çok dosyada sunucu tarafı işleme ve çağırma OpenReadStream kullanma. Sorunu çözmek için bu makalenin
LazyBrowserFileStream
Sunucu tarafı işleme ile dosyaları sunucuya yükleme bölümünde açıklanan sınıfını ve yaklaşımı kullanın.
Ek kaynaklar
ASP.NET Core