Nahrání souborů v ASP.NET CoreUpload files in ASP.NET Core

Steve Smith a Rutgerá zaplaveníBy Steve Smith and Rutger Storm

ASP.NET Core podporuje nahrávání jednoho nebo více souborů pomocí vazby modelu ve vyrovnávací paměti pro menší soubory a streamování bez vyrovnávací paměti pro větší soubory.ASP.NET Core supports uploading one or more files using buffered model binding for smaller files and unbuffered streaming for larger files.

Zobrazit nebo stáhnout ukázkový kód (Jak stáhnout)View or download sample code (how to download)

Důležité informace o zabezpečeníSecurity considerations

Pokud chcete uživatelům poskytnout možnost nahrávat soubory na server, buďte opatrní.Use caution when providing users with the ability to upload files to a server. Útočníci se můžou pokusit:Attackers may attempt to:

  • Vykoná útok DOS (Denial of Service ).Execute denial of service attacks.
  • Nahrání virů nebo malwaruUpload viruses or malware.
  • Narušit sítě a servery jinými způsoby.Compromise networks and servers in other ways.

Bezpečnostní kroky, které snižují pravděpodobnost úspěšného útoku, jsou:Security steps that reduce the likelihood of a successful attack are:

  • Nahrajte soubory do vyhrazené oblasti pro nahrávání souborů, nejlépe do nesystémové jednotky.Upload files to a dedicated file upload area, preferably to a non-system drive. Vyhrazené umístění usnadňuje omezení zabezpečení pro nahrané soubory.A dedicated location makes it easier to impose security restrictions on uploaded files. Zakažte oprávnění EXECUTE pro umístění pro nahrání souboru.†Disable execute permissions on the file upload location.†
  • Neuchovávat nahrané soubory ve stejném stromu adresářů jako aplikace. notDo not persist uploaded files in the same directory tree as the app.†
  • Použijte název bezpečného souboru určený aplikací.Use a safe file name determined by the app. Nepoužívejte název souboru poskytnutý uživatelem nebo nedůvěryhodného názvu nahraného souboru. † HTML při zobrazení kódování názvu nedůvěryhodného souboru.Don't use a file name provided by the user or the untrusted file name of the uploaded file.† HTML encode the untrusted file name when displaying it. Například protokolování názvu souboru nebo zobrazení v uživatelském rozhraní ( Razor Automatické kódování HTML kódování).For example, logging the file name or displaying in UI (Razor automatically HTML encodes output).
  • Povolte pro specifikaci návrhu aplikace jenom schválené přípony souborů.†Allow only approved file extensions for the app's design specification.†
  • Ověřte, zda jsou na serveru provedeny kontroly na straně klienta. † Kontroly na straně klienta je snadné obejít.Verify that client-side checks are performed on the server.† Client-side checks are easy to circumvent.
  • Ověřte velikost nahraného souboru.Check the size of an uploaded file. Nastavte limit maximální velikosti, aby se zabránilo velkým nahrávání.†Set a maximum size limit to prevent large uploads.†
  • Pokud by soubory neměly být přepsány nahraným souborem se stejným názvem, před nahráním souboru ověřte název souboru proti databázi nebo fyzickému úložišti.When files shouldn't be overwritten by an uploaded file with the same name, check the file name against the database or physical storage before uploading the file.
  • Před uložením souboru spusťte v nahraném obsahu skener virů nebo malwaru.Run a virus/malware scanner on uploaded content before the file is stored.

†Ukázková aplikace předvádí přístup, který splňuje kritéria.†The sample app demonstrates an approach that meets the criteria.

Upozornění

Nahrávání škodlivého kódu do systému je často prvním krokem ke spuštění kódu, který může:Uploading malicious code to a system is frequently the first step to executing code that can:

  • Zcela získá kontrolu nad systémem.Completely gain control of a system.
  • Přetížit systém s výsledkem, že dojde k chybě systému.Overload a system with the result that the system crashes.
  • Napadnout data uživatelů nebo systémových dat.Compromise user or system data.
  • Použijte graffiti pro veřejné uživatelské rozhraní.Apply graffiti to a public UI.

Informace o omezení oblasti útoku při přijímání souborů uživateli najdete v následujících zdrojích informací:For information on reducing the attack surface area when accepting files from users, see the following resources:

Další informace o implementaci bezpečnostních opatření, včetně příkladů z ukázkové aplikace, najdete v části ověření .For more information on implementing security measures, including examples from the sample app, see the Validation section.

Scénáře úložištěStorage scenarios

Mezi běžné možnosti úložiště pro soubory patří:Common storage options for files include:

  • DatabázeDatabase

    • U malých nahrávání souborů je databáze často rychlejší než možnosti fyzického úložiště (systému souborů nebo síťového sdílení).For small file uploads, a database is often faster than physical storage (file system or network share) options.
    • Databáze je často pohodlnější než možnosti fyzického úložiště, protože načtení záznamu databáze pro uživatelská data může současně poskytovat obsah souboru (například obrázek miniatury).A database is often more convenient than physical storage options because retrieval of a database record for user data can concurrently supply the file content (for example, an avatar image).
    • Databáze je potenciálně levnější než použití služby úložiště dat.A database is potentially less expensive than using a data storage service.
  • Fyzické úložiště (systém souborů nebo síťová sdílená složka)Physical storage (file system or network share)

    • Pro nahrávání velkých souborů:For large file uploads:
      • Omezení databáze mohou omezit velikost nahrávání.Database limits may restrict the size of the upload.
      • Fyzické úložiště je často méně hospodárné než úložiště v databázi.Physical storage is often less economical than storage in a database.
    • Fyzické úložiště je potenciálně levnější než použití služby úložiště dat.Physical storage is potentially less expensive than using a data storage service.
    • Proces aplikace musí mít oprávnění ke čtení a zápisu do umístění úložiště.The app's process must have read and write permissions to the storage location. Nikdy neudělujte oprávnění EXECUTE.Never grant execute permission.
  • Služba úložiště dat (například Azure Blob Storage)Data storage service (for example, Azure Blob Storage)

    • Služby obvykle nabízejí vylepšenou škálovatelnost a odolnost proti místním řešením, které obvykle podléhají jednomu bodu selhání.Services usually offer improved scalability and resiliency over on-premises solutions that are usually subject to single points of failure.
    • Služby jsou potenciálně nižší náklady ve scénářích infrastruktury velkých úložišť.Services are potentially lower cost in large storage infrastructure scenarios.

    Další informace najdete v tématu rychlý Start: použití .NET k vytvoření objektu BLOB v úložišti objektů.For more information, see Quickstart: Use .NET to create a blob in object storage.

Scénáře nahrávání souborůFile upload scenarios

Dva obecné přístupy k nahrávání souborů jsou ukládání do vyrovnávací paměti a streamování.Two general approaches for uploading files are buffering and streaming.

Do vyrovnávací pamětiBuffering

Celý soubor je načten do IFormFile , což je reprezentace souboru, který se používá ke zpracování nebo uložení souboru v jazyce C#.The entire file is read into an IFormFile, which is a C# representation of the file used to process or save the file.

Prostředky (disk, paměť) používané při nahrávání souborů závisí na počtu a velikosti souběžných nahrávání souborů.The resources (disk, memory) used by file uploads depend on the number and size of concurrent file uploads. Pokud se aplikace pokusí do vyrovnávací paměti příliš mnoho nahrávání, dojde k selhání lokality, když dojde k vynechání paměti nebo místa na disku.If an app attempts to buffer too many uploads, the site crashes when it runs out of memory or disk space. Pokud velikost nebo frekvence nahrávání souborů vyčerpá prostředky aplikace, použijte streamování.If the size or frequency of file uploads is exhausting app resources, use streaming.

Poznámka

Z paměti do dočasného souboru na disku se přesune libovolný soubor s vyrovnávací pamětí větší než 64 KB.Any single buffered file exceeding 64 KB is moved from memory to a temp file on disk.

Ukládání malých souborů do vyrovnávací paměti je popsáno v následujících částech tohoto tématu:Buffering small files is covered in the following sections of this topic:

StreamováníStreaming

Soubor se přijímá z požadavku na více částí a přímo se zpracovává nebo ukládá v aplikaci.The file is received from a multipart request and directly processed or saved by the app. Streamování nijak významně nezvyšuje výkon.Streaming doesn't improve performance significantly. Streamování snižuje nároky na paměť nebo místo na disku při nahrávání souborů.Streaming reduces the demands for memory or disk space when uploading files.

Streamování velkých souborů je zahrnuté v části nahrávání velkých souborů pomocí streamování .Streaming large files is covered in the Upload large files with streaming section.

Nahrávání malých souborů s vazbou modelu ve vyrovnávací paměti na fyzické úložištěUpload small files with buffered model binding to physical storage

Pro nahrání malých souborů použijte formulář s více částmi nebo sestavte požadavek POST pomocí JavaScriptu.To upload small files, use a multipart form or construct a POST request using JavaScript.

Následující příklad ukazuje použití Razor formuláře stránky k nahrání jednoho souboru (Pages/BufferedSingleFileUploadPhysical. cshtml do ukázkové aplikace):The following example demonstrates the use of a Razor Pages form to upload a single file (Pages/BufferedSingleFileUploadPhysical.cshtml in the sample app):

<form enctype="multipart/form-data" method="post">
    <dl>
        <dt>
            <label asp-for="FileUpload.FormFile"></label>
        </dt>
        <dd>
            <input asp-for="FileUpload.FormFile" type="file">
            <span asp-validation-for="FileUpload.FormFile"></span>
        </dd>
    </dl>
    <input asp-page-handler="Upload" class="btn" type="submit" value="Upload" />
</form>

Následující příklad je podobný předchozímu příkladu s tím rozdílem, že:The following example is analogous to the prior example except that:

  • K odeslání dat formuláře se používá JavaScript (Fetch API).JavaScript's (Fetch API) is used to submit the form's data.
  • Neexistuje žádné ověření.There's no validation.
<form action="BufferedSingleFileUploadPhysical/?handler=Upload" 
      enctype="multipart/form-data" onsubmit="AJAXSubmit(this);return false;" 
      method="post">
    <dl>
        <dt>
            <label for="FileUpload_FormFile">File</label>
        </dt>
        <dd>
            <input id="FileUpload_FormFile" type="file" 
                name="FileUpload.FormFile" />
        </dd>
    </dl>

    <input class="btn" type="submit" value="Upload" />

    <div style="margin-top:15px">
        <output name="result"></output>
    </div>
</form>

<script>
  async function AJAXSubmit (oFormElement) {
    var resultElement = oFormElement.elements.namedItem("result");
    const formData = new FormData(oFormElement);

    try {
    const response = await fetch(oFormElement.action, {
      method: 'POST',
      body: formData
    });

    if (response.ok) {
      window.location.href = '/';
    }

    resultElement.value = 'Result: ' + response.status + ' ' + 
      response.statusText;
    } catch (error) {
      console.error('Error:', error);
    }
  }
</script>

Chcete-li provést příspěvek formuláře v jazyce JavaScript pro klienty, kteří nepodporují rozhraní API pro načítání, použijte jeden z následujících přístupů:To perform the form POST in JavaScript for clients that don't support the Fetch API, use one of the following approaches:

  • Použijte načtenou výplň (například window. Fetch Fill (GitHub/Fetch)).Use a Fetch Polyfill (for example, window.fetch polyfill (github/fetch)).

  • Použijte XMLHttpRequest.Use XMLHttpRequest. Příklad:For example:

    <script>
      "use strict";
    
      function AJAXSubmit (oFormElement) {
        var oReq = new XMLHttpRequest();
        oReq.onload = function(e) { 
        oFormElement.elements.namedItem("result").value = 
          'Result: ' + this.status + ' ' + this.statusText;
        };
        oReq.open("post", oFormElement.action);
        oReq.send(new FormData(oFormElement));
      }
    </script>
    

Aby bylo možné podporovat nahrávání souborů, musí formuláře HTML určovat typ kódování ( enctype ) multipart/form-data .In order to support file uploads, HTML forms must specify an encoding type (enctype) of multipart/form-data.

Pro files vstupní element, který podporuje nahrávání více souborů, poskytněte multiple atribut <input> elementu:For a files input element to support uploading multiple files provide the multiple attribute on the <input> element:

<input asp-for="FileUpload.FormFiles" type="file" multiple>

Jednotlivé soubory nahrané na server jsou k dispozici prostřednictvím vazby modelu pomocí IFormFile .The individual files uploaded to the server can be accessed through Model Binding using IFormFile. Ukázková aplikace ukazuje více ukládání souborů do vyrovnávací paměti pro scénáře databáze a fyzických úložišť.The sample app demonstrates multiple buffered file uploads for database and physical storage scenarios.

Upozornění

Nepoužívejte not FileName vlastnost IFormFile jinou než pro zobrazení a protokolování.Do not use the FileName property of IFormFile other than for display and logging. Při zobrazení nebo protokolování je název souboru kódován HTML.When displaying or logging, HTML encode the file name. Útočník může poskytnout škodlivý název souboru, včetně úplných cest nebo relativních cest.An attacker can provide a malicious filename, including full paths or relative paths. Aplikace by měly:Applications should:

  • Odeberte cestu z názvu souboru zadaného uživatelem.Remove the path from the user-supplied filename.
  • Uložte název souboru s příponou PATH s kódováním HTML pro uživatelské rozhraní nebo protokolování.Save the HTML-encoded, path-removed filename for UI or logging.
  • Vygenerujte nový náhodný název souboru pro úložiště.Generate a new random filename for storage.

Následující kód odstraní cestu z názvu souboru:The following code removes the path from the file name:

string untrustedFileName = Path.GetFileName(pathName);

Zde uvedené příklady neberou ohled na zabezpečení.The examples provided thus far don't take into account security considerations. Další informace jsou k dispozici v následujících částech a ukázkové aplikaci:Additional information is provided by the following sections and the sample app:

Při nahrávání souborů pomocí vazby modelu a IFormFile může metoda Action přijmout:When uploading files using model binding and IFormFile, the action method can accept:

Poznámka

Vazba odpovídá souborům formuláře podle názvu.Binding matches form files by name. Například hodnota HTML v se name <input type="file" name="formFile"> musí shodovat s parametrem nebo vazbou vlastnosti jazyka C# ( FormFile ).For example, the HTML name value in <input type="file" name="formFile"> must match the C# parameter/property bound (FormFile). Další informace naleznete v části název atributu matched na název parametru metody post .For more information, see the Match name attribute value to parameter name of POST method section.

Následující příklad:The following example:

  • Projde jedním nebo více nahranými soubory.Loops through one or more uploaded files.
  • Pomocí Path. GetTempFileName vrátí úplnou cestu k souboru, včetně názvu souboru.Uses Path.GetTempFileName to return a full path for a file, including the file name.
  • Uloží soubory do místního systému souborů pomocí názvu souboru generovaného aplikací.Saves the files to the local file system using a file name generated by the app.
  • Vrátí celkový počet nahraných souborů a jejich velikost.Returns the total number and size of files uploaded.
public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> files)
{
    long size = files.Sum(f => f.Length);

    foreach (var formFile in files)
    {
        if (formFile.Length > 0)
        {
            var filePath = Path.GetTempFileName();

            using (var stream = System.IO.File.Create(filePath))
            {
                await formFile.CopyToAsync(stream);
            }
        }
    }

    // Process uploaded files
    // Don't rely on or trust the FileName property without validation.

    return Ok(new { count = files.Count, size });
}

Slouží Path.GetRandomFileName k vygenerování názvu souboru bez cesty.Use Path.GetRandomFileName to generate a file name without a path. V následujícím příkladu je cesta získána z konfigurace:In the following example, the path is obtained from configuration:

foreach (var formFile in files)
{
    if (formFile.Length > 0)
    {
        var filePath = Path.Combine(_config["StoredFilesPath"], 
            Path.GetRandomFileName());

        using (var stream = System.IO.File.Create(filePath))
        {
            await formFile.CopyToAsync(stream);
        }
    }
}

Cesta předaná do FileStream musí zahrnovat název souboru.The path passed to the FileStream must include the file name. Pokud není zadán název souboru, UnauthorizedAccessException je vyvolána za běhu.If the file name isn't provided, an UnauthorizedAccessException is thrown at runtime.

Soubory odeslané pomocí IFormFile techniky jsou v paměti nebo na disku na serveru před zpracováním uloženy do vyrovnávací paměti.Files uploaded using the IFormFile technique are buffered in memory or on disk on the server before processing. V rámci metody Action je IFormFile obsah přístupný jako Stream .Inside the action method, the IFormFile contents are accessible as a Stream. Kromě místního systému souborů je možné soubory ukládat do síťové sdílené složky nebo do služby úložiště souborů, jako je Azure Blob Storage.In addition to the local file system, files can be saved to a network share or to a file storage service, such as Azure Blob storage.

Další příklad, který projde několik souborů pro nahrání a používá bezpečné názvy souborů, najdete v ukázkové aplikaci v části Pages/BufferedMultipleFileUploadPhysical. cshtml. cs .For another example that loops over multiple files for upload and uses safe file names, see Pages/BufferedMultipleFileUploadPhysical.cshtml.cs in the sample app.

Upozornění

Cesta. GetTempFileName vyvolá výjimku, IOException Pokud jsou vytvořeny více než 65 535 souborů bez odstranění předchozích dočasných souborů.Path.GetTempFileName throws an IOException if more than 65,535 files are created without deleting previous temporary files. Limit 65 535 souborů je omezen na server.The limit of 65,535 files is a per-server limit. Další informace o tomto limitu pro operační systém Windows najdete v následujících tématech:For more information on this limit on Windows OS, see the remarks in the following topics:

Nahrávání malých souborů s vazbou modelu s vyrovnávací pamětí do databázeUpload small files with buffered model binding to a database

Chcete-li uložit data binárního souboru do databáze pomocí Entity Framework, definujte v Byte entitě vlastnost Array:To store binary file data in a database using Entity Framework, define a Byte array property on the entity:

public class AppFile
{
    public int Id { get; set; }
    public byte[] Content { get; set; }
}

Zadejte vlastnost modelu stránky pro třídu, která obsahuje IFormFile :Specify a page model property for the class that includes an IFormFile:

public class BufferedSingleFileUploadDbModel : PageModel
{
    ...

    [BindProperty]
    public BufferedSingleFileUploadDb FileUpload { get; set; }

    ...
}

public class BufferedSingleFileUploadDb
{
    [Required]
    [Display(Name="File")]
    public IFormFile FormFile { get; set; }
}

Poznámka

IFormFile lze ji použít přímo jako parametr metody akce nebo jako vlastnost vázaného modelu.IFormFile can be used directly as an action method parameter or as a bound model property. Předchozí příklad používá vlastnost vázaného modelu.The prior example uses a bound model property.

FileUploadJe použit ve Razor formuláři stránky:The FileUpload is used in the Razor Pages form:

<form enctype="multipart/form-data" method="post">
    <dl>
        <dt>
            <label asp-for="FileUpload.FormFile"></label>
        </dt>
        <dd>
            <input asp-for="FileUpload.FormFile" type="file">
        </dd>
    </dl>
    <input asp-page-handler="Upload" class="btn" type="submit" value="Upload">
</form>

Když je formulář publikovaný na serveru, zkopírujte IFormFile ho do datového proudu a uložte ho jako pole bajtů v databázi.When the form is POSTed to the server, copy the IFormFile to a stream and save it as a byte array in the database. V následujícím příkladu _dbContext ukládá kontext databáze aplikace:In the following example, _dbContext stores the app's database context:

public async Task<IActionResult> OnPostUploadAsync()
{
    using (var memoryStream = new MemoryStream())
    {
        await FileUpload.FormFile.CopyToAsync(memoryStream);

        // Upload the file if less than 2 MB
        if (memoryStream.Length < 2097152)
        {
            var file = new AppFile()
            {
                Content = memoryStream.ToArray()
            };

            _dbContext.File.Add(file);

            await _dbContext.SaveChangesAsync();
        }
        else
        {
            ModelState.AddModelError("File", "The file is too large.");
        }
    }

    return Page();
}

Předchozí příklad je podobný scénáři, který je znázorněný v ukázkové aplikaci:The preceding example is similar to a scenario demonstrated in the sample app:

  • Pages/BufferedSingleFileUploadDb. cshtmlPages/BufferedSingleFileUploadDb.cshtml
  • Pages/BufferedSingleFileUploadDb. cshtml. csPages/BufferedSingleFileUploadDb.cshtml.cs

Upozornění

Při ukládání binárních dat do relačních databází buďte opatrní, protože to může mít nepříznivý vliv na výkon.Use caution when storing binary data in relational databases, as it can adversely impact performance.

Nespoléhá se na nebo důvěřujete FileName vlastnosti IFormFile bez ověření.Don't rely on or trust the FileName property of IFormFile without validation. FileNameVlastnost by měla být použita pouze pro účely zobrazení a pouze po kódování HTML.The FileName property should only be used for display purposes and only after HTML encoding.

Uvedené příklady nevezmou ohled na zabezpečení.The examples provided don't take into account security considerations. Další informace jsou k dispozici v následujících částech a ukázkové aplikaci:Additional information is provided by the following sections and the sample app:

Nahrávání velkých souborů pomocí streamováníUpload large files with streaming

Následující příklad ukazuje, jak použít JavaScript ke streamování souboru do akce kontroleru.The following example demonstrates how to use JavaScript to stream a file to a controller action. Token proti padělání souboru se generuje pomocí vlastního atributu filtru a předává se do hlaviček protokolu HTTP klienta místo v textu žádosti.The file's antiforgery token is generated using a custom filter attribute and passed to the client HTTP headers instead of in the request body. Vzhledem k tomu, že metoda akce zpracovává nahraná data přímo, vazba modelu formuláře je zakázána jiným vlastním filtrem.Because the action method processes the uploaded data directly, form model binding is disabled by another custom filter. V rámci akce je obsah formuláře čten pomocí MultipartReader , který čte každou jednotlivou osobu MultipartSection , zpracovává soubor nebo ukládá obsah podle potřeby.Within the action, the form's contents are read using a MultipartReader, which reads each individual MultipartSection, processing the file or storing the contents as appropriate. Po načtení oddílů s více částmi provede akce vlastní vazbu modelu.After the multipart sections are read, the action performs its own model binding.

Počáteční odpověď stránky načte formulář a uloží token proti padělání do cookie (prostřednictvím GenerateAntiforgeryTokenCookieAttribute atributu).The initial page response loads the form and saves an antiforgery token in a cookie (via the GenerateAntiforgeryTokenCookieAttribute attribute). Atribut používá ASP.NET Core integrovanou podporu proti padělání pro nastavení cookie s tokenem požadavku:The attribute uses ASP.NET Core's built-in antiforgery support to set a cookie with a request token:

public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext context)
    {
        var antiforgery = context.HttpContext.RequestServices.GetService<IAntiforgery>();

        // Send the request token as a JavaScript-readable cookie
        var tokens = antiforgery.GetAndStoreTokens(context.HttpContext);

        context.HttpContext.Response.Cookies.Append(
            "RequestVerificationToken",
            tokens.RequestToken,
            new CookieOptions() { HttpOnly = false });
    }

    public override void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

DisableFormValueModelBindingAttributeSlouží k zakázání vazby modelu:The DisableFormValueModelBindingAttribute is used to disable model binding:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        var factories = context.ValueProviderFactories;
        factories.RemoveType<FormValueProviderFactory>();
        factories.RemoveType<FormFileValueProviderFactory>();
        factories.RemoveType<JQueryFormValueProviderFactory>();
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

V ukázkové aplikaci a používá GenerateAntiforgeryTokenCookieAttribute se DisableFormValueModelBindingAttribute jako filtr pro modelové aplikace stránky /StreamedSingleFileUploadDb a /StreamedSingleFileUploadPhysical v Startup.ConfigureServices Razor konvencích použití stránek:In the sample app, GenerateAntiforgeryTokenCookieAttribute and DisableFormValueModelBindingAttribute are applied as filters to the page application models of /StreamedSingleFileUploadDb and /StreamedSingleFileUploadPhysical in Startup.ConfigureServices using Razor Pages conventions:

services.AddRazorPages(options =>
{
    options.Conventions
        .AddPageApplicationModelConvention("/StreamedSingleFileUploadDb",
            model =>
            {
                model.Filters.Add(
                    new GenerateAntiforgeryTokenCookieAttribute());
                model.Filters.Add(
                    new DisableFormValueModelBindingAttribute());
            });
    options.Conventions
        .AddPageApplicationModelConvention("/StreamedSingleFileUploadPhysical",
            model =>
            {
                model.Filters.Add(
                    new GenerateAntiforgeryTokenCookieAttribute());
                model.Filters.Add(
                    new DisableFormValueModelBindingAttribute());
            });
});

Vzhledem k tomu, že vazba modelu nepřečte formulář, parametry, které jsou svázané z formuláře, se nezobrazují (budou pokračovat v práci s dotazem, trasou a hlavičkou).Since model binding doesn't read the form, parameters that are bound from the form don't bind (query, route, and header continue to work). Metoda Action pracuje přímo s Request vlastností.The action method works directly with the Request property. MultipartReaderSlouží ke čtení jednotlivých oddílů.A MultipartReader is used to read each section. Data klíč/hodnota jsou uložena v KeyValueAccumulator .Key/value data is stored in a KeyValueAccumulator. Po načtení oddílů s více částmi se obsah KeyValueAccumulator používá pro svázání dat formuláře s typem modelu.After the multipart sections are read, the contents of the KeyValueAccumulator are used to bind the form data to a model type.

Úplná StreamingController.UploadDatabase metoda pro streamování do databáze s EF Core:The complete StreamingController.UploadDatabase method for streaming to a database with EF Core:

[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadDatabase()
{
    if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
    {
        ModelState.AddModelError("File", 
            $"The request couldn't be processed (Error 1).");
        // Log error

        return BadRequest(ModelState);
    }

    // Accumulate the form data key-value pairs in the request (formAccumulator).
    var formAccumulator = new KeyValueAccumulator();
    var trustedFileNameForDisplay = string.Empty;
    var untrustedFileNameForStorage = string.Empty;
    var streamedFileContent = new byte[0];

    var boundary = MultipartRequestHelper.GetBoundary(
        MediaTypeHeaderValue.Parse(Request.ContentType),
        _defaultFormOptions.MultipartBoundaryLengthLimit);
    var reader = new MultipartReader(boundary, HttpContext.Request.Body);

    var section = await reader.ReadNextSectionAsync();

    while (section != null)
    {
        var hasContentDispositionHeader = 
            ContentDispositionHeaderValue.TryParse(
                section.ContentDisposition, out var contentDisposition);

        if (hasContentDispositionHeader)
        {
            if (MultipartRequestHelper
                .HasFileContentDisposition(contentDisposition))
            {
                untrustedFileNameForStorage = contentDisposition.FileName.Value;
                // Don't trust the file name sent by the client. To display
                // the file name, HTML-encode the value.
                trustedFileNameForDisplay = WebUtility.HtmlEncode(
                        contentDisposition.FileName.Value);

                streamedFileContent = 
                    await FileHelpers.ProcessStreamedFile(section, contentDisposition, 
                        ModelState, _permittedExtensions, _fileSizeLimit);

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }
            }
            else if (MultipartRequestHelper
                .HasFormDataContentDisposition(contentDisposition))
            {
                // Don't limit the key name length because the 
                // multipart headers length limit is already in effect.
                var key = HeaderUtilities
                    .RemoveQuotes(contentDisposition.Name).Value;
                var encoding = GetEncoding(section);

                if (encoding == null)
                {
                    ModelState.AddModelError("File", 
                        $"The request couldn't be processed (Error 2).");
                    // Log error

                    return BadRequest(ModelState);
                }

                using (var streamReader = new StreamReader(
                    section.Body,
                    encoding,
                    detectEncodingFromByteOrderMarks: true,
                    bufferSize: 1024,
                    leaveOpen: true))
                {
                    // The value length limit is enforced by 
                    // MultipartBodyLengthLimit
                    var value = await streamReader.ReadToEndAsync();

                    if (string.Equals(value, "undefined", 
                        StringComparison.OrdinalIgnoreCase))
                    {
                        value = string.Empty;
                    }

                    formAccumulator.Append(key, value);

                    if (formAccumulator.ValueCount > 
                        _defaultFormOptions.ValueCountLimit)
                    {
                        // Form key count limit of 
                        // _defaultFormOptions.ValueCountLimit 
                        // is exceeded.
                        ModelState.AddModelError("File", 
                            $"The request couldn't be processed (Error 3).");
                        // Log error

                        return BadRequest(ModelState);
                    }
                }
            }
        }

        // Drain any remaining section body that hasn't been consumed and
        // read the headers for the next section.
        section = await reader.ReadNextSectionAsync();
    }

    // Bind form data to the model
    var formData = new FormData();
    var formValueProvider = new FormValueProvider(
        BindingSource.Form,
        new FormCollection(formAccumulator.GetResults()),
        CultureInfo.CurrentCulture);
    var bindingSuccessful = await TryUpdateModelAsync(formData, prefix: "",
        valueProvider: formValueProvider);

    if (!bindingSuccessful)
    {
        ModelState.AddModelError("File", 
            "The request couldn't be processed (Error 5).");
        // Log error

        return BadRequest(ModelState);
    }

    // **WARNING!**
    // In the following example, the file is saved without
    // scanning the file's contents. In most production
    // scenarios, an anti-virus/anti-malware scanner API
    // is used on the file before making the file available
    // for download or for use by other systems. 
    // For more information, see the topic that accompanies 
    // this sample app.

    var file = new AppFile()
    {
        Content = streamedFileContent,
        UntrustedName = untrustedFileNameForStorage,
        Note = formData.Note,
        Size = streamedFileContent.Length, 
        UploadDT = DateTime.UtcNow
    };

    _context.File.Add(file);
    await _context.SaveChangesAsync();

    return Created(nameof(StreamingController), null);
}

MultipartRequestHelper (Nástroje/MultipartRequestHelper. cs):MultipartRequestHelper (Utilities/MultipartRequestHelper.cs):

using System;
using System.IO;
using Microsoft.Net.Http.Headers;

namespace SampleApp.Utilities
{
    public static class MultipartRequestHelper
    {
        // Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
        // The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit.
        public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
        {
            var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;

            if (string.IsNullOrWhiteSpace(boundary))
            {
                throw new InvalidDataException("Missing content-type boundary.");
            }

            if (boundary.Length > lengthLimit)
            {
                throw new InvalidDataException(
                    $"Multipart boundary length limit {lengthLimit} exceeded.");
            }

            return boundary;
        }

        public static bool IsMultipartContentType(string contentType)
        {
            return !string.IsNullOrEmpty(contentType)
                   && contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
        }

        public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
        {
            // Content-Disposition: form-data; name="key";
            return contentDisposition != null
                && contentDisposition.DispositionType.Equals("form-data")
                && string.IsNullOrEmpty(contentDisposition.FileName.Value)
                && string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
        }

        public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
        {
            // Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
            return contentDisposition != null
                && contentDisposition.DispositionType.Equals("form-data")
                && (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
                    || !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
        }
    }
}

Úplná StreamingController.UploadPhysical metoda pro streamování do fyzického umístění:The complete StreamingController.UploadPhysical method for streaming to a physical location:

[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadPhysical()
{
    if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
    {
        ModelState.AddModelError("File", 
            $"The request couldn't be processed (Error 1).");
        // Log error

        return BadRequest(ModelState);
    }

    var boundary = MultipartRequestHelper.GetBoundary(
        MediaTypeHeaderValue.Parse(Request.ContentType),
        _defaultFormOptions.MultipartBoundaryLengthLimit);
    var reader = new MultipartReader(boundary, HttpContext.Request.Body);
    var section = await reader.ReadNextSectionAsync();

    while (section != null)
    {
        var hasContentDispositionHeader = 
            ContentDispositionHeaderValue.TryParse(
                section.ContentDisposition, out var contentDisposition);

        if (hasContentDispositionHeader)
        {
            // This check assumes that there's a file
            // present without form data. If form data
            // is present, this method immediately fails
            // and returns the model error.
            if (!MultipartRequestHelper
                .HasFileContentDisposition(contentDisposition))
            {
                ModelState.AddModelError("File", 
                    $"The request couldn't be processed (Error 2).");
                // Log error

                return BadRequest(ModelState);
            }
            else
            {
                // Don't trust the file name sent by the client. To display
                // the file name, HTML-encode the value.
                var trustedFileNameForDisplay = WebUtility.HtmlEncode(
                        contentDisposition.FileName.Value);
                var trustedFileNameForFileStorage = Path.GetRandomFileName();

                // **WARNING!**
                // In the following example, the file is saved without
                // scanning the file's contents. In most production
                // scenarios, an anti-virus/anti-malware scanner API
                // is used on the file before making the file available
                // for download or for use by other systems. 
                // For more information, see the topic that accompanies 
                // this sample.

                var streamedFileContent = await FileHelpers.ProcessStreamedFile(
                    section, contentDisposition, ModelState, 
                    _permittedExtensions, _fileSizeLimit);

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }

                using (var targetStream = System.IO.File.Create(
                    Path.Combine(_targetFilePath, trustedFileNameForFileStorage)))
                {
                    await targetStream.WriteAsync(streamedFileContent);

                    _logger.LogInformation(
                        "Uploaded file '{TrustedFileNameForDisplay}' saved to " +
                        "'{TargetFilePath}' as {TrustedFileNameForFileStorage}", 
                        trustedFileNameForDisplay, _targetFilePath, 
                        trustedFileNameForFileStorage);
                }
            }
        }

        // Drain any remaining section body that hasn't been consumed and
        // read the headers for the next section.
        section = await reader.ReadNextSectionAsync();
    }

    return Created(nameof(StreamingController), null);
}

V ukázkové aplikaci jsou kontroly ověřování zpracovávány nástrojem FileHelpers.ProcessStreamedFile .In the sample app, validation checks are handled by FileHelpers.ProcessStreamedFile.

OvěřováníValidation

Třída ukázkové aplikace FileHelpers ukazuje několik kontrol ukládání souborů do vyrovnávací paměti IFormFile a datových proudů při nahrávání.The sample app's FileHelpers class demonstrates a several checks for buffered IFormFile and streamed file uploads. Informace o zpracování IFormFile ukládání souborů do vyrovnávací paměti v ukázkové aplikaci naleznete v ProcessFormFile metodě v souboru Utilities/Helper. cs .For processing IFormFile buffered file uploads in the sample app, see the ProcessFormFile method in the Utilities/FileHelpers.cs file. Pro zpracování streamované soubory se podívejte na ProcessStreamedFile metodu ve stejném souboru.For processing streamed files, see the ProcessStreamedFile method in the same file.

Upozornění

Metody zpracování ověřování, které jsou znázorněné v ukázkové aplikaci, nekontrolují obsah nahraných souborů.The validation processing methods demonstrated in the sample app don't scan the content of uploaded files. Ve většině produkčních scénářů se v souboru používá rozhraní API pro skenování virů nebo malwaru, než je soubor dostupný uživatelům nebo jiným systémům.In most production scenarios, a virus/malware scanner API is used on the file before making the file available to users or other systems.

I když ukázka tématu poskytuje pracovní příklad technik ověřování, Neimplementujte FileHelpers třídu v produkční aplikaci, pokud:Although the topic sample provides a working example of validation techniques, don't implement the FileHelpers class in a production app unless you:

  • Plně rozumíte implementaci.Fully understand the implementation.
  • Upravte implementaci podle potřeby pro prostředí a specifikace aplikace.Modify the implementation as appropriate for the app's environment and specifications.

Nikdy nepoužívejte nerozlišený kód zabezpečení v aplikaci bez nutnosti řešit tyto požadavky.Never indiscriminately implement security code in an app without addressing these requirements.

Ověření obsahuContent validation

Pro nahraný obsah použijte rozhraní API pro kontrolu virů a malwaru třetí strany.Use a third party virus/malware scanning API on uploaded content.

Prohledávání souborů je náročné na prostředky serveru ve scénářích s vysokým objemem.Scanning files is demanding on server resources in high volume scenarios. Pokud dojde ke snížení výkonu zpracování požadavků z důvodu kontroly souborů, zvažte přesměrování práce skenování na službu na pozadí, případně služby spuštěné na serveru, který se liší od serveru aplikace.If request processing performance is diminished due to file scanning, consider offloading the scanning work to a background service, possibly a service running on a server different from the app's server. Nahrané soubory se obvykle uchovávají v oblasti v karanténě, dokud je kontrola virů na pozadí nevrátí.Typically, uploaded files are held in a quarantined area until the background virus scanner checks them. Když soubor projde, soubor se přesune do normálního umístění úložiště souborů.When a file passes, the file is moved to the normal file storage location. Tyto kroky se obvykle provádí ve spojení s databázovým záznamem, který indikuje stav kontroly souboru.These steps are usually performed in conjunction with a database record that indicates the scanning status of a file. Při použití takového přístupu zůstane aplikace a Server App zaměřené na reakci na požadavky.By using such an approach, the app and app server remain focused on responding to requests.

Ověření přípony souboruFile extension validation

Přípona nahraného souboru by měla být zaškrtnutá na seznamu povolených rozšíření.The uploaded file's extension should be checked against a list of permitted extensions. Příklad:For example:

private string[] permittedExtensions = { ".txt", ".pdf" };

var ext = Path.GetExtension(uploadedFileName).ToLowerInvariant();

if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
{
    // The extension is invalid ... discontinue processing the file
}

Ověření podpisu souboruFile signature validation

Podpis souboru se určuje na prvních několika bajtech na začátku souboru.A file's signature is determined by the first few bytes at the start of a file. Tyto bajty lze použít k určení, zda přípona odpovídá obsahu souboru.These bytes can be used to indicate if the extension matches the content of the file. Ukázková aplikace zkontroluje podpisy souborů pro několik běžných typů souborů.The sample app checks file signatures for a few common file types. V následujícím příkladu se pro tento soubor kontroluje podpis souboru obrázku JPEG:In the following example, the file signature for a JPEG image is checked against the file:

private static readonly Dictionary<string, List<byte[]>> _fileSignature = 
    new Dictionary<string, List<byte[]>>
{
    { ".jpeg", new List<byte[]>
        {
            new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
            new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
            new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 },
        }
    },
};

using (var reader = new BinaryReader(uploadedFileData))
{
    var signatures = _fileSignature[ext];
    var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));

    return signatures.Any(signature => 
        headerBytes.Take(signature.Length).SequenceEqual(signature));
}

Další signatury souborů získáte v dokumentaci signatury souborů a oficiálních souborů.To obtain additional file signatures, see the File Signatures Database and official file specifications.

Zabezpečení názvu souboruFile name security

Nikdy nepoužívejte název souboru dodaný klientem pro uložení souboru do fyzického úložiště.Never use a client-supplied file name for saving a file to physical storage. Vytvořte bezpečný název souboru pro soubor pomocí cesty. GetRandomFileName nebo cesty. GetTempFileName a vytvořte úplnou cestu (včetně názvu souboru) pro dočasné úložiště.Create a safe file name for the file using Path.GetRandomFileName or Path.GetTempFileName to create a full path (including the file name) for temporary storage.

Razor Automatické kódování HTML kóduje hodnoty vlastností pro zobrazení.Razor automatically HTML encodes property values for display. Následující kód je bezpečné použít:The following code is safe to use:

@foreach (var file in Model.DatabaseFiles) {
    <tr>
        <td>
            @file.UntrustedName
        </td>
    </tr>
}

Mimo Razor , vždy HtmlEncode obsah názvu souboru z požadavku uživatele.Outside of Razor, always HtmlEncode file name content from a user's request.

Mnoho implementací musí zahrnovat kontrolu, že soubor existuje. v opačném případě je soubor přepsán souborem se stejným názvem.Many implementations must include a check that the file exists; otherwise, the file is overwritten by a file of the same name. Poskytněte další logiku pro splnění specifikací vaší aplikace.Supply additional logic to meet your app's specifications.

Ověřování velikostiSize validation

Omezte velikost nahraných souborů.Limit the size of uploaded files.

V ukázkové aplikaci je velikost souboru omezená na 2 MB (uvedené v bajtech).In the sample app, the size of the file is limited to 2 MB (indicated in bytes). Limit se poskytuje prostřednictvím Konfigurace z appsettings.jsv souboru:The limit is supplied via Configuration from the appsettings.json file:

{
  "FileSizeLimit": 2097152
}

FileSizeLimitTřída je vložena do PageModel tříd:The FileSizeLimit is injected into PageModel classes:

public class BufferedSingleFileUploadPhysicalModel : PageModel
{
    private readonly long _fileSizeLimit;

    public BufferedSingleFileUploadPhysicalModel(IConfiguration config)
    {
        _fileSizeLimit = config.GetValue<long>("FileSizeLimit");
    }

    ...
}

Když velikost souboru překročí limit, soubor se odmítne:When a file size exceeds the limit, the file is rejected:

if (formFile.Length > _fileSizeLimit)
{
    // The file is too large ... discontinue processing the file
}

Porovnává hodnotu atributu name s parametrem název metody POSTMatch name attribute value to parameter name of POST method

V Razor neformulářích, které publikují data formuláře nebo přímo využívají JavaScript FormData , název zadaný v prvku formuláře nebo FormData musí odpovídat názvu parametru v akci kontroleru.In non-Razor forms that POST form data or use JavaScript's FormData directly, the name specified in the form's element or FormData must match the name of the parameter in the controller's action.

V následujícím příkladu:In the following example:

  • Při použití <input> elementu name je atribut nastaven na hodnotu battlePlans :When using an <input> element, the name attribute is set to the value battlePlans:

    <input type="file" name="battlePlans" multiple>
    
  • Při použití FormData v jazyce JavaScript je název nastaven na hodnotu battlePlans :When using FormData in JavaScript, the name is set to the value battlePlans:

    var formData = new FormData();
    
    for (var file in files) {
      formData.append("battlePlans", file, file.name);
    }
    

Pro parametr metody jazyka C# () použijte stejný název battlePlans :Use a matching name for the parameter of the C# method (battlePlans):

  • Pro Razor metodu obslužné rutiny stránky stránky s názvem Upload :For a Razor Pages page handler method named Upload:

    public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> battlePlans)
    
  • Pro metodu akce po kontroléru MVC:For an MVC POST controller action method:

    public async Task<IActionResult> Post(List<IFormFile> battlePlans)
    

Konfigurace serveru a aplikaceServer and app configuration

Omezení délky těla částíMultipart body length limit

MultipartBodyLengthLimit nastaví limit délky jednotlivých částí části.MultipartBodyLengthLimit sets the limit for the length of each multipart body. Oddíly formuláře, které překračují toto omezení, vyvolávají InvalidDataException při analýze.Form sections that exceed this limit throw an InvalidDataException when parsed. Výchozí hodnota je 134 217 728 (128 MB).The default is 134,217,728 (128 MB). Upravte limit pomocí MultipartBodyLengthLimit nastavení v Startup.ConfigureServices :Customize the limit using the MultipartBodyLengthLimit setting in Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<FormOptions>(options =>
    {
        // Set the limit to 256 MB
        options.MultipartBodyLengthLimit = 268435456;
    });
}

RequestFormLimitsAttribute slouží k nastavení MultipartBodyLengthLimit pro jednu stránku nebo akci.RequestFormLimitsAttribute is used to set the MultipartBodyLengthLimit for a single page or action.

V Razor aplikaci Pages použijte filtr s konvencí v nástroji Startup.ConfigureServices :In a Razor Pages app, apply the filter with a convention in Startup.ConfigureServices:

services.AddRazorPages(options =>
{
    options.Conventions
        .AddPageApplicationModelConvention("/FileUploadPage",
            model.Filters.Add(
                new RequestFormLimitsAttribute()
                {
                    // Set the limit to 256 MB
                    MultipartBodyLengthLimit = 268435456
                });
});

V Razor aplikaci Pages nebo aplikaci MVC použijte filtr na model stránky nebo metodu akce:In a Razor Pages app or an MVC app, apply the filter to the page model or action method:

// Set the limit to 256 MB
[RequestFormLimits(MultipartBodyLengthLimit = 268435456)]
public class BufferedSingleFileUploadPhysicalModel : PageModel
{
    ...
}

Kestrel maximální velikost textu požadavkuKestrel maximum request body size

Pro aplikace hostované v Kestrel je výchozí maximální velikost textu požadavku 30 000 000 bajtů, což je přibližně 28,6 MB.For apps hosted by Kestrel, the default maximum request body size is 30,000,000 bytes, which is approximately 28.6 MB. Přizpůsobte limit pomocí možnosti serveru MaxRequestBodySize Kestrel:Customize the limit using the MaxRequestBodySize Kestrel server option:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel((context, options) =>
            {
                // Handle requests up to 50 MB
                options.Limits.MaxRequestBodySize = 52428800;
            })
            .UseStartup<Startup>();
        });

RequestSizeLimitAttribute slouží k nastavení MaxRequestBodySize pro jednu stránku nebo akci.RequestSizeLimitAttribute is used to set the MaxRequestBodySize for a single page or action.

V Razor aplikaci Pages použijte filtr s konvencí v nástroji Startup.ConfigureServices :In a Razor Pages app, apply the filter with a convention in Startup.ConfigureServices:

services.AddRazorPages(options =>
{
    options.Conventions
        .AddPageApplicationModelConvention("/FileUploadPage",
            model =>
            {
                // Handle requests up to 50 MB
                model.Filters.Add(
                    new RequestSizeLimitAttribute(52428800));
            });
});

V Razor aplikaci Pages nebo aplikaci MVC použijte filtr na třídu obslužné rutiny stránky nebo na metodu akce:In a Razor pages app or an MVC app, apply the filter to the page handler class or action method:

// Handle requests up to 50 MB
[RequestSizeLimit(52428800)]
public class BufferedSingleFileUploadPhysicalModel : PageModel
{
    ...
}

RequestSizeLimitAttributeLze také použít pomocí @attribute Razor direktivy:The RequestSizeLimitAttribute can also be applied using the @attribute Razor directive:

@attribute [RequestSizeLimitAttribute(52428800)]

Další omezení KestrelOther Kestrel limits

Pro aplikace hostované v Kestrel se můžou vztahovat další omezení Kestrel:Other Kestrel limits may apply for apps hosted by Kestrel:

IISIIS

Výchozí limit počtu požadavků ( maxAllowedContentLength ) je 30 000 000 bajtů, což je přibližně 28,6 MB.The default request limit (maxAllowedContentLength) is 30,000,000 bytes, which is approximately 28.6 MB. Přizpůsobte limit v web.config souboru.Customize the limit in the web.config file. V následujícím příkladu je limit nastaven na 50 MB (52 428 800 bajtů):In the following example, the limit is set to 50 MB (52,428,800 bytes):

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="52428800" />
    </requestFiltering>
  </security>
</system.webServer>

maxAllowedContentLengthNastavení platí pouze pro službu IIS.The maxAllowedContentLength setting only applies to IIS. Další informace najdete v tématu omezení <requestLimits> požadavků .For more information, see Request Limits <requestLimits>.

Řešení potížíTroubleshoot

Níže jsou uvedeny některé běžné problémy, které se vyskytly při práci s nahráváním souborů a jejich možnými řešeními.Below are some common problems encountered when working with uploading files and their possible solutions.

Při nasazení na server služby IIS se nenašla chyba.Not Found error when deployed to an IIS server

Následující chyba znamená, že nahraný soubor překračuje délku nakonfigurovaného obsahu serveru:The following error indicates that the uploaded file exceeds the server's configured content length:

HTTP 404.13 - Not Found
The request filtering module is configured to deny a request that exceeds the request content length.

Další informace najdete v části IIS .For more information, see the IIS section.

Chyba připojeníConnection failure

Chyba připojení a připojení k serveru pro resetování pravděpodobně znamená, že nahraný soubor překračuje maximální velikost textu požadavku Kestrel.A connection error and a reset server connection probably indicates that the uploaded file exceeds Kestrel's maximum request body size. Další informace najdete v části Kestrel maximální velikost textu požadavku .For more information, see the Kestrel maximum request body size section. Omezení připojení klientů Kestrel mohou také vyžadovat úpravu.Kestrel client connection limits may also require adjustment.

Výjimka odkazu s hodnotou null s IFormFileNull Reference Exception with IFormFile

Pokud kontroler přijímá odeslané soubory pomocí IFormFile , ale hodnota je null , potvrďte, že formulář HTML určuje enctype hodnotu multipart/form-data .If the controller is accepting uploaded files using IFormFile but the value is null, confirm that the HTML form is specifying an enctype value of multipart/form-data. Pokud tento atribut není nastaven na <form> elementu, odeslání souboru neproběhne a jakékoli vázané IFormFile argumenty jsou null .If this attribute isn't set on the <form> element, the file upload doesn't occur and any bound IFormFile arguments are null. Ujistěte se také, že nahrávání názvů v datech formuláře odpovídá pojmenování aplikace.Also confirm that the upload naming in form data matches the app's naming.

Proud je příliš dlouhý.Stream was too long

Příklady v tomto tématu MemoryStream se spoléhají na uchovávání obsahu nahraného souboru.The examples in this topic rely upon MemoryStream to hold the uploaded file's content. Omezení velikosti MemoryStream je int.MaxValue .The size limit of a MemoryStream is int.MaxValue. Pokud scénář nahrávání souborů aplikace vyžaduje, aby byl obsah souboru větší než 50 MB, použijte alternativní přístup, který nespoléhá na jednu z nich MemoryStream pro uchovávání obsahu nahraného souboru.If the app's file upload scenario requires holding file content larger than 50 MB, use an alternative approach that doesn't rely upon a single MemoryStream for holding an uploaded file's content.

ASP.NET Core podporuje nahrávání jednoho nebo více souborů pomocí vazby modelu ve vyrovnávací paměti pro menší soubory a streamování bez vyrovnávací paměti pro větší soubory.ASP.NET Core supports uploading one or more files using buffered model binding for smaller files and unbuffered streaming for larger files.

Zobrazit nebo stáhnout ukázkový kód (Jak stáhnout)View or download sample code (how to download)

Důležité informace o zabezpečeníSecurity considerations

Pokud chcete uživatelům poskytnout možnost nahrávat soubory na server, buďte opatrní.Use caution when providing users with the ability to upload files to a server. Útočníci se můžou pokusit:Attackers may attempt to:

  • Vykoná útok DOS (Denial of Service ).Execute denial of service attacks.
  • Nahrání virů nebo malwaruUpload viruses or malware.
  • Narušit sítě a servery jinými způsoby.Compromise networks and servers in other ways.

Bezpečnostní kroky, které snižují pravděpodobnost úspěšného útoku, jsou:Security steps that reduce the likelihood of a successful attack are:

  • Nahrajte soubory do vyhrazené oblasti pro nahrávání souborů, nejlépe do nesystémové jednotky.Upload files to a dedicated file upload area, preferably to a non-system drive. Vyhrazené umístění usnadňuje omezení zabezpečení pro nahrané soubory.A dedicated location makes it easier to impose security restrictions on uploaded files. Zakažte oprávnění EXECUTE pro umístění pro nahrání souboru.†Disable execute permissions on the file upload location.†
  • Neuchovávat nahrané soubory ve stejném stromu adresářů jako aplikace. notDo not persist uploaded files in the same directory tree as the app.†
  • Použijte název bezpečného souboru určený aplikací.Use a safe file name determined by the app. Nepoužívejte název souboru poskytnutý uživatelem nebo nedůvěryhodného názvu nahraného souboru. † HTML při zobrazení kódování názvu nedůvěryhodného souboru.Don't use a file name provided by the user or the untrusted file name of the uploaded file.† HTML encode the untrusted file name when displaying it. Například protokolování názvu souboru nebo zobrazení v uživatelském rozhraní ( Razor Automatické kódování HTML kódování).For example, logging the file name or displaying in UI (Razor automatically HTML encodes output).
  • Povolte pro specifikaci návrhu aplikace jenom schválené přípony souborů.†Allow only approved file extensions for the app's design specification.†
  • Ověřte, zda jsou na serveru provedeny kontroly na straně klienta. † Kontroly na straně klienta je snadné obejít.Verify that client-side checks are performed on the server.† Client-side checks are easy to circumvent.
  • Ověřte velikost nahraného souboru.Check the size of an uploaded file. Nastavte limit maximální velikosti, aby se zabránilo velkým nahrávání.†Set a maximum size limit to prevent large uploads.†
  • Pokud by soubory neměly být přepsány nahraným souborem se stejným názvem, před nahráním souboru ověřte název souboru proti databázi nebo fyzickému úložišti.When files shouldn't be overwritten by an uploaded file with the same name, check the file name against the database or physical storage before uploading the file.
  • Před uložením souboru spusťte v nahraném obsahu skener virů nebo malwaru.Run a virus/malware scanner on uploaded content before the file is stored.

†Ukázková aplikace předvádí přístup, který splňuje kritéria.†The sample app demonstrates an approach that meets the criteria.

Upozornění

Nahrávání škodlivého kódu do systému je často prvním krokem ke spuštění kódu, který může:Uploading malicious code to a system is frequently the first step to executing code that can:

  • Zcela získá kontrolu nad systémem.Completely gain control of a system.
  • Přetížit systém s výsledkem, že dojde k chybě systému.Overload a system with the result that the system crashes.
  • Napadnout data uživatelů nebo systémových dat.Compromise user or system data.
  • Použijte graffiti pro veřejné uživatelské rozhraní.Apply graffiti to a public UI.

Informace o omezení oblasti útoku při přijímání souborů uživateli najdete v následujících zdrojích informací:For information on reducing the attack surface area when accepting files from users, see the following resources:

Další informace o implementaci bezpečnostních opatření, včetně příkladů z ukázkové aplikace, najdete v části ověření .For more information on implementing security measures, including examples from the sample app, see the Validation section.

Scénáře úložištěStorage scenarios

Mezi běžné možnosti úložiště pro soubory patří:Common storage options for files include:

  • DatabázeDatabase

    • U malých nahrávání souborů je databáze často rychlejší než možnosti fyzického úložiště (systému souborů nebo síťového sdílení).For small file uploads, a database is often faster than physical storage (file system or network share) options.
    • Databáze je často pohodlnější než možnosti fyzického úložiště, protože načtení záznamu databáze pro uživatelská data může současně poskytovat obsah souboru (například obrázek miniatury).A database is often more convenient than physical storage options because retrieval of a database record for user data can concurrently supply the file content (for example, an avatar image).
    • Databáze je potenciálně levnější než použití služby úložiště dat.A database is potentially less expensive than using a data storage service.
  • Fyzické úložiště (systém souborů nebo síťová sdílená složka)Physical storage (file system or network share)

    • Pro nahrávání velkých souborů:For large file uploads:
      • Omezení databáze mohou omezit velikost nahrávání.Database limits may restrict the size of the upload.
      • Fyzické úložiště je často méně hospodárné než úložiště v databázi.Physical storage is often less economical than storage in a database.
    • Fyzické úložiště je potenciálně levnější než použití služby úložiště dat.Physical storage is potentially less expensive than using a data storage service.
    • Proces aplikace musí mít oprávnění ke čtení a zápisu do umístění úložiště.The app's process must have read and write permissions to the storage location. Nikdy neudělujte oprávnění EXECUTE.Never grant execute permission.
  • Služba úložiště dat (například Azure Blob Storage)Data storage service (for example, Azure Blob Storage)

    • Služby obvykle nabízejí vylepšenou škálovatelnost a odolnost proti místním řešením, které obvykle podléhají jednomu bodu selhání.Services usually offer improved scalability and resiliency over on-premises solutions that are usually subject to single points of failure.
    • Služby jsou potenciálně nižší náklady ve scénářích infrastruktury velkých úložišť.Services are potentially lower cost in large storage infrastructure scenarios.

    Další informace najdete v tématu rychlý Start: použití .NET k vytvoření objektu BLOB v úložišti objektů.For more information, see Quickstart: Use .NET to create a blob in object storage.

Scénáře nahrávání souborůFile upload scenarios

Dva obecné přístupy k nahrávání souborů jsou ukládání do vyrovnávací paměti a streamování.Two general approaches for uploading files are buffering and streaming.

Do vyrovnávací pamětiBuffering

Celý soubor je načten do IFormFile , což je reprezentace souboru, který se používá ke zpracování nebo uložení souboru v jazyce C#.The entire file is read into an IFormFile, which is a C# representation of the file used to process or save the file.

Prostředky (disk, paměť) používané při nahrávání souborů závisí na počtu a velikosti souběžných nahrávání souborů.The resources (disk, memory) used by file uploads depend on the number and size of concurrent file uploads. Pokud se aplikace pokusí do vyrovnávací paměti příliš mnoho nahrávání, dojde k selhání lokality, když dojde k vynechání paměti nebo místa na disku.If an app attempts to buffer too many uploads, the site crashes when it runs out of memory or disk space. Pokud velikost nebo frekvence nahrávání souborů vyčerpá prostředky aplikace, použijte streamování.If the size or frequency of file uploads is exhausting app resources, use streaming.

Poznámka

Z paměti do dočasného souboru na disku se přesune libovolný soubor s vyrovnávací pamětí větší než 64 KB.Any single buffered file exceeding 64 KB is moved from memory to a temp file on disk.

Ukládání malých souborů do vyrovnávací paměti je popsáno v následujících částech tohoto tématu:Buffering small files is covered in the following sections of this topic:

StreamováníStreaming

Soubor se přijímá z požadavku na více částí a přímo se zpracovává nebo ukládá v aplikaci.The file is received from a multipart request and directly processed or saved by the app. Streamování nijak významně nezvyšuje výkon.Streaming doesn't improve performance significantly. Streamování snižuje nároky na paměť nebo místo na disku při nahrávání souborů.Streaming reduces the demands for memory or disk space when uploading files.

Streamování velkých souborů je zahrnuté v části nahrávání velkých souborů pomocí streamování .Streaming large files is covered in the Upload large files with streaming section.

Nahrávání malých souborů s vazbou modelu ve vyrovnávací paměti na fyzické úložištěUpload small files with buffered model binding to physical storage

Pro nahrání malých souborů použijte formulář s více částmi nebo sestavte požadavek POST pomocí JavaScriptu.To upload small files, use a multipart form or construct a POST request using JavaScript.

Následující příklad ukazuje použití Razor formuláře stránky k nahrání jednoho souboru (Pages/BufferedSingleFileUploadPhysical. cshtml do ukázkové aplikace):The following example demonstrates the use of a Razor Pages form to upload a single file (Pages/BufferedSingleFileUploadPhysical.cshtml in the sample app):

<form enctype="multipart/form-data" method="post">
    <dl>
        <dt>
            <label asp-for="FileUpload.FormFile"></label>
        </dt>
        <dd>
            <input asp-for="FileUpload.FormFile" type="file">
            <span asp-validation-for="FileUpload.FormFile"></span>
        </dd>
    </dl>
    <input asp-page-handler="Upload" class="btn" type="submit" value="Upload" />
</form>

Následující příklad je podobný předchozímu příkladu s tím rozdílem, že:The following example is analogous to the prior example except that:

  • K odeslání dat formuláře se používá JavaScript (Fetch API).JavaScript's (Fetch API) is used to submit the form's data.
  • Neexistuje žádné ověření.There's no validation.
<form action="BufferedSingleFileUploadPhysical/?handler=Upload" 
      enctype="multipart/form-data" onsubmit="AJAXSubmit(this);return false;" 
      method="post">
    <dl>
        <dt>
            <label for="FileUpload_FormFile">File</label>
        </dt>
        <dd>
            <input id="FileUpload_FormFile" type="file" 
                name="FileUpload.FormFile" />
        </dd>
    </dl>

    <input class="btn" type="submit" value="Upload" />

    <div style="margin-top:15px">
        <output name="result"></output>
    </div>
</form>

<script>
  async function AJAXSubmit (oFormElement) {
    var resultElement = oFormElement.elements.namedItem("result");
    const formData = new FormData(oFormElement);

    try {
    const response = await fetch(oFormElement.action, {
      method: 'POST',
      body: formData
    });

    if (response.ok) {
      window.location.href = '/';
    }

    resultElement.value = 'Result: ' + response.status + ' ' + 
      response.statusText;
    } catch (error) {
      console.error('Error:', error);
    }
  }
</script>

Chcete-li provést příspěvek formuláře v jazyce JavaScript pro klienty, kteří nepodporují rozhraní API pro načítání, použijte jeden z následujících přístupů:To perform the form POST in JavaScript for clients that don't support the Fetch API, use one of the following approaches:

  • Použijte načtenou výplň (například window. Fetch Fill (GitHub/Fetch)).Use a Fetch Polyfill (for example, window.fetch polyfill (github/fetch)).

  • Použijte XMLHttpRequest.Use XMLHttpRequest. Příklad:For example:

    <script>
      "use strict";
    
      function AJAXSubmit (oFormElement) {
        var oReq = new XMLHttpRequest();
        oReq.onload = function(e) { 
        oFormElement.elements.namedItem("result").value = 
          'Result: ' + this.status + ' ' + this.statusText;
        };
        oReq.open("post", oFormElement.action);
        oReq.send(new FormData(oFormElement));
      }
    </script>
    

Aby bylo možné podporovat nahrávání souborů, musí formuláře HTML určovat typ kódování ( enctype ) multipart/form-data .In order to support file uploads, HTML forms must specify an encoding type (enctype) of multipart/form-data.

Pro files vstupní element, který podporuje nahrávání více souborů, poskytněte multiple atribut <input> elementu:For a files input element to support uploading multiple files provide the multiple attribute on the <input> element:

<input asp-for="FileUpload.FormFiles" type="file" multiple>

Jednotlivé soubory nahrané na server jsou k dispozici prostřednictvím vazby modelu pomocí IFormFile .The individual files uploaded to the server can be accessed through Model Binding using IFormFile. Ukázková aplikace ukazuje více ukládání souborů do vyrovnávací paměti pro scénáře databáze a fyzických úložišť.The sample app demonstrates multiple buffered file uploads for database and physical storage scenarios.

Upozornění

Nepoužívejte not FileName vlastnost IFormFile jinou než pro zobrazení a protokolování.Do not use the FileName property of IFormFile other than for display and logging. Při zobrazení nebo protokolování je název souboru kódován HTML.When displaying or logging, HTML encode the file name. Útočník může poskytnout škodlivý název souboru, včetně úplných cest nebo relativních cest.An attacker can provide a malicious filename, including full paths or relative paths. Aplikace by měly:Applications should:

  • Odeberte cestu z názvu souboru zadaného uživatelem.Remove the path from the user-supplied filename.
  • Uložte název souboru s příponou PATH s kódováním HTML pro uživatelské rozhraní nebo protokolování.Save the HTML-encoded, path-removed filename for UI or logging.
  • Vygenerujte nový náhodný název souboru pro úložiště.Generate a new random filename for storage.

Následující kód odstraní cestu z názvu souboru:The following code removes the path from the file name:

string untrustedFileName = Path.GetFileName(pathName);

Zde uvedené příklady neberou ohled na zabezpečení.The examples provided thus far don't take into account security considerations. Další informace jsou k dispozici v následujících částech a ukázkové aplikaci:Additional information is provided by the following sections and the sample app:

Při nahrávání souborů pomocí vazby modelu a IFormFile může metoda Action přijmout:When uploading files using model binding and IFormFile, the action method can accept:

Poznámka

Vazba odpovídá souborům formuláře podle názvu.Binding matches form files by name. Například hodnota HTML v se name <input type="file" name="formFile"> musí shodovat s parametrem nebo vazbou vlastnosti jazyka C# ( FormFile ).For example, the HTML name value in <input type="file" name="formFile"> must match the C# parameter/property bound (FormFile). Další informace naleznete v části název atributu matched na název parametru metody post .For more information, see the Match name attribute value to parameter name of POST method section.

Následující příklad:The following example:

  • Projde jedním nebo více nahranými soubory.Loops through one or more uploaded files.
  • Pomocí Path. GetTempFileName vrátí úplnou cestu k souboru, včetně názvu souboru.Uses Path.GetTempFileName to return a full path for a file, including the file name.
  • Uloží soubory do místního systému souborů pomocí názvu souboru generovaného aplikací.Saves the files to the local file system using a file name generated by the app.
  • Vrátí celkový počet nahraných souborů a jejich velikost.Returns the total number and size of files uploaded.
public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> files)
{
    long size = files.Sum(f => f.Length);

    foreach (var formFile in files)
    {
        if (formFile.Length > 0)
        {
            var filePath = Path.GetTempFileName();

            using (var stream = System.IO.File.Create(filePath))
            {
                await formFile.CopyToAsync(stream);
            }
        }
    }

    // Process uploaded files
    // Don't rely on or trust the FileName property without validation.

    return Ok(new { count = files.Count, size });
}

Slouží Path.GetRandomFileName k vygenerování názvu souboru bez cesty.Use Path.GetRandomFileName to generate a file name without a path. V následujícím příkladu je cesta získána z konfigurace:In the following example, the path is obtained from configuration:

foreach (var formFile in files)
{
    if (formFile.Length > 0)
    {
        var filePath = Path.Combine(_config["StoredFilesPath"], 
            Path.GetRandomFileName());

        using (var stream = System.IO.File.Create(filePath))
        {
            await formFile.CopyToAsync(stream);
        }
    }
}

Cesta předaná do FileStream musí zahrnovat název souboru.The path passed to the FileStream must include the file name. Pokud není zadán název souboru, UnauthorizedAccessException je vyvolána za běhu.If the file name isn't provided, an UnauthorizedAccessException is thrown at runtime.

Soubory odeslané pomocí IFormFile techniky jsou v paměti nebo na disku na serveru před zpracováním uloženy do vyrovnávací paměti.Files uploaded using the IFormFile technique are buffered in memory or on disk on the server before processing. V rámci metody Action je IFormFile obsah přístupný jako Stream .Inside the action method, the IFormFile contents are accessible as a Stream. Kromě místního systému souborů je možné soubory ukládat do síťové sdílené složky nebo do služby úložiště souborů, jako je Azure Blob Storage.In addition to the local file system, files can be saved to a network share or to a file storage service, such as Azure Blob storage.

Další příklad, který projde několik souborů pro nahrání a používá bezpečné názvy souborů, najdete v ukázkové aplikaci v části Pages/BufferedMultipleFileUploadPhysical. cshtml. cs .For another example that loops over multiple files for upload and uses safe file names, see Pages/BufferedMultipleFileUploadPhysical.cshtml.cs in the sample app.

Upozornění

Cesta. GetTempFileName vyvolá výjimku, IOException Pokud jsou vytvořeny více než 65 535 souborů bez odstranění předchozích dočasných souborů.Path.GetTempFileName throws an IOException if more than 65,535 files are created without deleting previous temporary files. Limit 65 535 souborů je omezen na server.The limit of 65,535 files is a per-server limit. Další informace o tomto limitu pro operační systém Windows najdete v následujících tématech:For more information on this limit on Windows OS, see the remarks in the following topics:

Nahrávání malých souborů s vazbou modelu s vyrovnávací pamětí do databázeUpload small files with buffered model binding to a database

Chcete-li uložit data binárního souboru do databáze pomocí Entity Framework, definujte v Byte entitě vlastnost Array:To store binary file data in a database using Entity Framework, define a Byte array property on the entity:

public class AppFile
{
    public int Id { get; set; }
    public byte[] Content { get; set; }
}

Zadejte vlastnost modelu stránky pro třídu, která obsahuje IFormFile :Specify a page model property for the class that includes an IFormFile:

public class BufferedSingleFileUploadDbModel : PageModel
{
    ...

    [BindProperty]
    public BufferedSingleFileUploadDb FileUpload { get; set; }

    ...
}

public class BufferedSingleFileUploadDb
{
    [Required]
    [Display(Name="File")]
    public IFormFile FormFile { get; set; }
}

Poznámka

IFormFile lze ji použít přímo jako parametr metody akce nebo jako vlastnost vázaného modelu.IFormFile can be used directly as an action method parameter or as a bound model property. Předchozí příklad používá vlastnost vázaného modelu.The prior example uses a bound model property.

FileUploadJe použit ve Razor formuláři stránky:The FileUpload is used in the Razor Pages form:

<form enctype="multipart/form-data" method="post">
    <dl>
        <dt>
            <label asp-for="FileUpload.FormFile"></label>
        </dt>
        <dd>
            <input asp-for="FileUpload.FormFile" type="file">
        </dd>
    </dl>
    <input asp-page-handler="Upload" class="btn" type="submit" value="Upload">
</form>

Když je formulář publikovaný na serveru, zkopírujte IFormFile ho do datového proudu a uložte ho jako pole bajtů v databázi.When the form is POSTed to the server, copy the IFormFile to a stream and save it as a byte array in the database. V následujícím příkladu _dbContext ukládá kontext databáze aplikace:In the following example, _dbContext stores the app's database context:

public async Task<IActionResult> OnPostUploadAsync()
{
    using (var memoryStream = new MemoryStream())
    {
        await FileUpload.FormFile.CopyToAsync(memoryStream);

        // Upload the file if less than 2 MB
        if (memoryStream.Length < 2097152)
        {
            var file = new AppFile()
            {
                Content = memoryStream.ToArray()
            };

            _dbContext.File.Add(file);

            await _dbContext.SaveChangesAsync();
        }
        else
        {
            ModelState.AddModelError("File", "The file is too large.");
        }
    }

    return Page();
}

Předchozí příklad je podobný scénáři, který je znázorněný v ukázkové aplikaci:The preceding example is similar to a scenario demonstrated in the sample app:

  • Pages/BufferedSingleFileUploadDb. cshtmlPages/BufferedSingleFileUploadDb.cshtml
  • Pages/BufferedSingleFileUploadDb. cshtml. csPages/BufferedSingleFileUploadDb.cshtml.cs

Upozornění

Při ukládání binárních dat do relačních databází buďte opatrní, protože to může mít nepříznivý vliv na výkon.Use caution when storing binary data in relational databases, as it can adversely impact performance.

Nespoléhá se na nebo důvěřujete FileName vlastnosti IFormFile bez ověření.Don't rely on or trust the FileName property of IFormFile without validation. FileNameVlastnost by měla být použita pouze pro účely zobrazení a pouze po kódování HTML.The FileName property should only be used for display purposes and only after HTML encoding.

Uvedené příklady nevezmou ohled na zabezpečení.The examples provided don't take into account security considerations. Další informace jsou k dispozici v následujících částech a ukázkové aplikaci:Additional information is provided by the following sections and the sample app:

Nahrávání velkých souborů pomocí streamováníUpload large files with streaming

Následující příklad ukazuje, jak použít JavaScript ke streamování souboru do akce kontroleru.The following example demonstrates how to use JavaScript to stream a file to a controller action. Token proti padělání souboru se generuje pomocí vlastního atributu filtru a předává se do hlaviček protokolu HTTP klienta místo v textu žádosti.The file's antiforgery token is generated using a custom filter attribute and passed to the client HTTP headers instead of in the request body. Vzhledem k tomu, že metoda akce zpracovává nahraná data přímo, vazba modelu formuláře je zakázána jiným vlastním filtrem.Because the action method processes the uploaded data directly, form model binding is disabled by another custom filter. V rámci akce je obsah formuláře čten pomocí MultipartReader , který čte každou jednotlivou osobu MultipartSection , zpracovává soubor nebo ukládá obsah podle potřeby.Within the action, the form's contents are read using a MultipartReader, which reads each individual MultipartSection, processing the file or storing the contents as appropriate. Po načtení oddílů s více částmi provede akce vlastní vazbu modelu.After the multipart sections are read, the action performs its own model binding.

Počáteční odpověď stránky načte formulář a uloží token proti padělání do cookie (prostřednictvím GenerateAntiforgeryTokenCookieAttribute atributu).The initial page response loads the form and saves an antiforgery token in a cookie (via the GenerateAntiforgeryTokenCookieAttribute attribute). Atribut používá ASP.NET Core integrovanou podporu proti padělání pro nastavení cookie s tokenem požadavku:The attribute uses ASP.NET Core's built-in antiforgery support to set a cookie with a request token:

public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext context)
    {
        var antiforgery = context.HttpContext.RequestServices.GetService<IAntiforgery>();

        // Send the request token as a JavaScript-readable cookie
        var tokens = antiforgery.GetAndStoreTokens(context.HttpContext);

        context.HttpContext.Response.Cookies.Append(
            "RequestVerificationToken",
            tokens.RequestToken,
            new CookieOptions() { HttpOnly = false });
    }

    public override void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

DisableFormValueModelBindingAttributeSlouží k zakázání vazby modelu:The DisableFormValueModelBindingAttribute is used to disable model binding:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        var factories = context.ValueProviderFactories;
        factories.RemoveType<FormValueProviderFactory>();
        factories.RemoveType<FormFileValueProviderFactory>();
        factories.RemoveType<JQueryFormValueProviderFactory>();
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

V ukázkové aplikaci a používá GenerateAntiforgeryTokenCookieAttribute se DisableFormValueModelBindingAttribute jako filtr pro modelové aplikace stránky /StreamedSingleFileUploadDb a /StreamedSingleFileUploadPhysical v Startup.ConfigureServices Razor konvencích použití stránek:In the sample app, GenerateAntiforgeryTokenCookieAttribute and DisableFormValueModelBindingAttribute are applied as filters to the page application models of /StreamedSingleFileUploadDb and /StreamedSingleFileUploadPhysical in Startup.ConfigureServices using Razor Pages conventions:

services.AddRazorPages(options =>
{
    options.Conventions
        .AddPageApplicationModelConvention("/StreamedSingleFileUploadDb",
            model =>
            {
                model.Filters.Add(
                    new GenerateAntiforgeryTokenCookieAttribute());
                model.Filters.Add(
                    new DisableFormValueModelBindingAttribute());
            });
    options.Conventions
        .AddPageApplicationModelConvention("/StreamedSingleFileUploadPhysical",
            model =>
            {
                model.Filters.Add(
                    new GenerateAntiforgeryTokenCookieAttribute());
                model.Filters.Add(
                    new DisableFormValueModelBindingAttribute());
            });
});

Vzhledem k tomu, že vazba modelu nepřečte formulář, parametry, které jsou svázané z formuláře, se nezobrazují (budou pokračovat v práci s dotazem, trasou a hlavičkou).Since model binding doesn't read the form, parameters that are bound from the form don't bind (query, route, and header continue to work). Metoda Action pracuje přímo s Request vlastností.The action method works directly with the Request property. MultipartReaderSlouží ke čtení jednotlivých oddílů.A MultipartReader is used to read each section. Data klíč/hodnota jsou uložena v KeyValueAccumulator .Key/value data is stored in a KeyValueAccumulator. Po načtení oddílů s více částmi se obsah KeyValueAccumulator používá pro svázání dat formuláře s typem modelu.After the multipart sections are read, the contents of the KeyValueAccumulator are used to bind the form data to a model type.

Úplná StreamingController.UploadDatabase metoda pro streamování do databáze s EF Core:The complete StreamingController.UploadDatabase method for streaming to a database with EF Core:

[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadDatabase()
{
    if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
    {
        ModelState.AddModelError("File", 
            $"The request couldn't be processed (Error 1).");
        // Log error

        return BadRequest(ModelState);
    }

    // Accumulate the form data key-value pairs in the request (formAccumulator).
    var formAccumulator = new KeyValueAccumulator();
    var trustedFileNameForDisplay = string.Empty;
    var untrustedFileNameForStorage = string.Empty;
    var streamedFileContent = new byte[0];

    var boundary = MultipartRequestHelper.GetBoundary(
        MediaTypeHeaderValue.Parse(Request.ContentType),
        _defaultFormOptions.MultipartBoundaryLengthLimit);
    var reader = new MultipartReader(boundary, HttpContext.Request.Body);

    var section = await reader.ReadNextSectionAsync();

    while (section != null)
    {
        var hasContentDispositionHeader = 
            ContentDispositionHeaderValue.TryParse(
                section.ContentDisposition, out var contentDisposition);

        if (hasContentDispositionHeader)
        {
            if (MultipartRequestHelper
                .HasFileContentDisposition(contentDisposition))
            {
                untrustedFileNameForStorage = contentDisposition.FileName.Value;
                // Don't trust the file name sent by the client. To display
                // the file name, HTML-encode the value.
                trustedFileNameForDisplay = WebUtility.HtmlEncode(
                        contentDisposition.FileName.Value);

                streamedFileContent = 
                    await FileHelpers.ProcessStreamedFile(section, contentDisposition, 
                        ModelState, _permittedExtensions, _fileSizeLimit);

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }
            }
            else if (MultipartRequestHelper
                .HasFormDataContentDisposition(contentDisposition))
            {
                // Don't limit the key name length because the 
                // multipart headers length limit is already in effect.
                var key = HeaderUtilities
                    .RemoveQuotes(contentDisposition.Name).Value;
                var encoding = GetEncoding(section);

                if (encoding == null)
                {
                    ModelState.AddModelError("File", 
                        $"The request couldn't be processed (Error 2).");
                    // Log error

                    return BadRequest(ModelState);
                }

                using (var streamReader = new StreamReader(
                    section.Body,
                    encoding,
                    detectEncodingFromByteOrderMarks: true,
                    bufferSize: 1024,
                    leaveOpen: true))
                {
                    // The value length limit is enforced by 
                    // MultipartBodyLengthLimit
                    var value = await streamReader.ReadToEndAsync();

                    if (string.Equals(value, "undefined", 
                        StringComparison.OrdinalIgnoreCase))
                    {
                        value = string.Empty;
                    }

                    formAccumulator.Append(key, value);

                    if (formAccumulator.ValueCount > 
                        _defaultFormOptions.ValueCountLimit)
                    {
                        // Form key count limit of 
                        // _defaultFormOptions.ValueCountLimit 
                        // is exceeded.
                        ModelState.AddModelError("File", 
                            $"The request couldn't be processed (Error 3).");
                        // Log error

                        return BadRequest(ModelState);
                    }
                }
            }
        }

        // Drain any remaining section body that hasn't been consumed and
        // read the headers for the next section.
        section = await reader.ReadNextSectionAsync();
    }

    // Bind form data to the model
    var formData = new FormData();
    var formValueProvider = new FormValueProvider(
        BindingSource.Form,
        new FormCollection(formAccumulator.GetResults()),
        CultureInfo.CurrentCulture);
    var bindingSuccessful = await TryUpdateModelAsync(formData, prefix: "",
        valueProvider: formValueProvider);

    if (!bindingSuccessful)
    {
        ModelState.AddModelError("File", 
            "The request couldn't be processed (Error 5).");
        // Log error

        return BadRequest(ModelState);
    }

    // **WARNING!**
    // In the following example, the file is saved without
    // scanning the file's contents. In most production
    // scenarios, an anti-virus/anti-malware scanner API
    // is used on the file before making the file available
    // for download or for use by other systems. 
    // For more information, see the topic that accompanies 
    // this sample app.

    var file = new AppFile()
    {
        Content = streamedFileContent,
        UntrustedName = untrustedFileNameForStorage,
        Note = formData.Note,
        Size = streamedFileContent.Length, 
        UploadDT = DateTime.UtcNow
    };

    _context.File.Add(file);
    await _context.SaveChangesAsync();

    return Created(nameof(StreamingController), null);
}

MultipartRequestHelper (Nástroje/MultipartRequestHelper. cs):MultipartRequestHelper (Utilities/MultipartRequestHelper.cs):

using System;
using System.IO;
using Microsoft.Net.Http.Headers;

namespace SampleApp.Utilities
{
    public static class MultipartRequestHelper
    {
        // Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
        // The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit.
        public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
        {
            var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;

            if (string.IsNullOrWhiteSpace(boundary))
            {
                throw new InvalidDataException("Missing content-type boundary.");
            }

            if (boundary.Length > lengthLimit)
            {
                throw new InvalidDataException(
                    $"Multipart boundary length limit {lengthLimit} exceeded.");
            }

            return boundary;
        }

        public static bool IsMultipartContentType(string contentType)
        {
            return !string.IsNullOrEmpty(contentType)
                   && contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
        }

        public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
        {
            // Content-Disposition: form-data; name="key";
            return contentDisposition != null
                && contentDisposition.DispositionType.Equals("form-data")
                && string.IsNullOrEmpty(contentDisposition.FileName.Value)
                && string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
        }

        public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
        {
            // Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
            return contentDisposition != null
                && contentDisposition.DispositionType.Equals("form-data")
                && (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
                    || !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
        }
    }
}

Úplná StreamingController.UploadPhysical metoda pro streamování do fyzického umístění:The complete StreamingController.UploadPhysical method for streaming to a physical location:

[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadPhysical()
{
    if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
    {
        ModelState.AddModelError("File", 
            $"The request couldn't be processed (Error 1).");
        // Log error

        return BadRequest(ModelState);
    }

    var boundary = MultipartRequestHelper.GetBoundary(
        MediaTypeHeaderValue.Parse(Request.ContentType),
        _defaultFormOptions.MultipartBoundaryLengthLimit);
    var reader = new MultipartReader(boundary, HttpContext.Request.Body);
    var section = await reader.ReadNextSectionAsync();

    while (section != null)
    {
        var hasContentDispositionHeader = 
            ContentDispositionHeaderValue.TryParse(
                section.ContentDisposition, out var contentDisposition);

        if (hasContentDispositionHeader)
        {
            // This check assumes that there's a file
            // present without form data. If form data
            // is present, this method immediately fails
            // and returns the model error.
            if (!MultipartRequestHelper
                .HasFileContentDisposition(contentDisposition))
            {
                ModelState.AddModelError("File", 
                    $"The request couldn't be processed (Error 2).");
                // Log error

                return BadRequest(ModelState);
            }
            else
            {
                // Don't trust the file name sent by the client. To display
                // the file name, HTML-encode the value.
                var trustedFileNameForDisplay = WebUtility.HtmlEncode(
                        contentDisposition.FileName.Value);
                var trustedFileNameForFileStorage = Path.GetRandomFileName();

                // **WARNING!**
                // In the following example, the file is saved without
                // scanning the file's contents. In most production
                // scenarios, an anti-virus/anti-malware scanner API
                // is used on the file before making the file available
                // for download or for use by other systems. 
                // For more information, see the topic that accompanies 
                // this sample.

                var streamedFileContent = await FileHelpers.ProcessStreamedFile(
                    section, contentDisposition, ModelState, 
                    _permittedExtensions, _fileSizeLimit);

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }

                using (var targetStream = System.IO.File.Create(
                    Path.Combine(_targetFilePath, trustedFileNameForFileStorage)))
                {
                    await targetStream.WriteAsync(streamedFileContent);

                    _logger.LogInformation(
                        "Uploaded file '{TrustedFileNameForDisplay}' saved to " +
                        "'{TargetFilePath}' as {TrustedFileNameForFileStorage}", 
                        trustedFileNameForDisplay, _targetFilePath, 
                        trustedFileNameForFileStorage);
                }
            }
        }

        // Drain any remaining section body that hasn't been consumed and
        // read the headers for the next section.
        section = await reader.ReadNextSectionAsync();
    }

    return Created(nameof(StreamingController), null);
}

V ukázkové aplikaci jsou kontroly ověřování zpracovávány nástrojem FileHelpers.ProcessStreamedFile .In the sample app, validation checks are handled by FileHelpers.ProcessStreamedFile.

OvěřováníValidation

Třída ukázkové aplikace FileHelpers ukazuje několik kontrol ukládání souborů do vyrovnávací paměti IFormFile a datových proudů při nahrávání.The sample app's FileHelpers class demonstrates a several checks for buffered IFormFile and streamed file uploads. Informace o zpracování IFormFile ukládání souborů do vyrovnávací paměti v ukázkové aplikaci naleznete v ProcessFormFile metodě v souboru Utilities/Helper. cs .For processing IFormFile buffered file uploads in the sample app, see the ProcessFormFile method in the Utilities/FileHelpers.cs file. Pro zpracování streamované soubory se podívejte na ProcessStreamedFile metodu ve stejném souboru.For processing streamed files, see the ProcessStreamedFile method in the same file.

Upozornění

Metody zpracování ověřování, které jsou znázorněné v ukázkové aplikaci, nekontrolují obsah nahraných souborů.The validation processing methods demonstrated in the sample app don't scan the content of uploaded files. Ve většině produkčních scénářů se v souboru používá rozhraní API pro skenování virů nebo malwaru, než je soubor dostupný uživatelům nebo jiným systémům.In most production scenarios, a virus/malware scanner API is used on the file before making the file available to users or other systems.

I když ukázka tématu poskytuje pracovní příklad technik ověřování, Neimplementujte FileHelpers třídu v produkční aplikaci, pokud:Although the topic sample provides a working example of validation techniques, don't implement the FileHelpers class in a production app unless you:

  • Plně rozumíte implementaci.Fully understand the implementation.
  • Upravte implementaci podle potřeby pro prostředí a specifikace aplikace.Modify the implementation as appropriate for the app's environment and specifications.

Nikdy nepoužívejte nerozlišený kód zabezpečení v aplikaci bez nutnosti řešit tyto požadavky.Never indiscriminately implement security code in an app without addressing these requirements.

Ověření obsahuContent validation

Pro nahraný obsah použijte rozhraní API pro kontrolu virů a malwaru třetí strany.Use a third party virus/malware scanning API on uploaded content.

Prohledávání souborů je náročné na prostředky serveru ve scénářích s vysokým objemem.Scanning files is demanding on server resources in high volume scenarios. Pokud dojde ke snížení výkonu zpracování požadavků z důvodu kontroly souborů, zvažte přesměrování práce skenování na službu na pozadí, případně služby spuštěné na serveru, který se liší od serveru aplikace.If request processing performance is diminished due to file scanning, consider offloading the scanning work to a background service, possibly a service running on a server different from the app's server. Nahrané soubory se obvykle uchovávají v oblasti v karanténě, dokud je kontrola virů na pozadí nevrátí.Typically, uploaded files are held in a quarantined area until the background virus scanner checks them. Když soubor projde, soubor se přesune do normálního umístění úložiště souborů.When a file passes, the file is moved to the normal file storage location. Tyto kroky se obvykle provádí ve spojení s databázovým záznamem, který indikuje stav kontroly souboru.These steps are usually performed in conjunction with a database record that indicates the scanning status of a file. Při použití takového přístupu zůstane aplikace a Server App zaměřené na reakci na požadavky.By using such an approach, the app and app server remain focused on responding to requests.

Ověření přípony souboruFile extension validation

Přípona nahraného souboru by měla být zaškrtnutá na seznamu povolených rozšíření.The uploaded file's extension should be checked against a list of permitted extensions. Příklad:For example:

private string[] permittedExtensions = { ".txt", ".pdf" };

var ext = Path.GetExtension(uploadedFileName).ToLowerInvariant();

if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
{
    // The extension is invalid ... discontinue processing the file
}

Ověření podpisu souboruFile signature validation

Podpis souboru se určuje na prvních několika bajtech na začátku souboru.A file's signature is determined by the first few bytes at the start of a file. Tyto bajty lze použít k určení, zda přípona odpovídá obsahu souboru.These bytes can be used to indicate if the extension matches the content of the file. Ukázková aplikace zkontroluje podpisy souborů pro několik běžných typů souborů.The sample app checks file signatures for a few common file types. V následujícím příkladu se pro tento soubor kontroluje podpis souboru obrázku JPEG:In the following example, the file signature for a JPEG image is checked against the file:

private static readonly Dictionary<string, List<byte[]>> _fileSignature = 
    new Dictionary<string, List<byte[]>>
{
    { ".jpeg", new List<byte[]>
        {
            new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
            new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
            new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 },
        }
    },
};

using (var reader = new BinaryReader(uploadedFileData))
{
    var signatures = _fileSignature[ext];
    var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));

    return signatures.Any(signature => 
        headerBytes.Take(signature.Length).SequenceEqual(signature));
}

Další signatury souborů získáte v dokumentaci signatury souborů a oficiálních souborů.To obtain additional file signatures, see the File Signatures Database and official file specifications.

Zabezpečení názvu souboruFile name security

Nikdy nepoužívejte název souboru dodaný klientem pro uložení souboru do fyzického úložiště.Never use a client-supplied file name for saving a file to physical storage. Vytvořte bezpečný název souboru pro soubor pomocí cesty. GetRandomFileName nebo cesty. GetTempFileName a vytvořte úplnou cestu (včetně názvu souboru) pro dočasné úložiště.Create a safe file name for the file using Path.GetRandomFileName or Path.GetTempFileName to create a full path (including the file name) for temporary storage.

Razor Automatické kódování HTML kóduje hodnoty vlastností pro zobrazení.Razor automatically HTML encodes property values for display. Následující kód je bezpečné použít:The following code is safe to use:

@foreach (var file in Model.DatabaseFiles) {
    <tr>
        <td>
            @file.UntrustedName
        </td>
    </tr>
}

Mimo Razor , vždy HtmlEncode obsah názvu souboru z požadavku uživatele.Outside of Razor, always HtmlEncode file name content from a user's request.

Mnoho implementací musí zahrnovat kontrolu, že soubor existuje. v opačném případě je soubor přepsán souborem se stejným názvem.Many implementations must include a check that the file exists; otherwise, the file is overwritten by a file of the same name. Poskytněte další logiku pro splnění specifikací vaší aplikace.Supply additional logic to meet your app's specifications.

Ověřování velikostiSize validation

Omezte velikost nahraných souborů.Limit the size of uploaded files.

V ukázkové aplikaci je velikost souboru omezená na 2 MB (uvedené v bajtech).In the sample app, the size of the file is limited to 2 MB (indicated in bytes). Limit se poskytuje prostřednictvím Konfigurace z appsettings.jsv souboru:The limit is supplied via Configuration from the appsettings.json file:

{
  "FileSizeLimit": 2097152
}

FileSizeLimitTřída je vložena do PageModel tříd:The FileSizeLimit is injected into PageModel classes:

public class BufferedSingleFileUploadPhysicalModel : PageModel
{
    private readonly long _fileSizeLimit;

    public BufferedSingleFileUploadPhysicalModel(IConfiguration config)
    {
        _fileSizeLimit = config.GetValue<long>("FileSizeLimit");
    }

    ...
}

Když velikost souboru překročí limit, soubor se odmítne:When a file size exceeds the limit, the file is rejected:

if (formFile.Length > _fileSizeLimit)
{
    // The file is too large ... discontinue processing the file
}

Porovnává hodnotu atributu name s parametrem název metody POSTMatch name attribute value to parameter name of POST method

V Razor neformulářích, které publikují data formuláře nebo přímo využívají JavaScript FormData , název zadaný v prvku formuláře nebo FormData musí odpovídat názvu parametru v akci kontroleru.In non-Razor forms that POST form data or use JavaScript's FormData directly, the name specified in the form's element or FormData must match the name of the parameter in the controller's action.

V následujícím příkladu:In the following example:

  • Při použití <input> elementu name je atribut nastaven na hodnotu battlePlans :When using an <input> element, the name attribute is set to the value battlePlans:

    <input type="file" name="battlePlans" multiple>
    
  • Při použití FormData v jazyce JavaScript je název nastaven na hodnotu battlePlans :When using FormData in JavaScript, the name is set to the value battlePlans:

    var formData = new FormData();
    
    for (var file in files) {
      formData.append("battlePlans", file, file.name);
    }
    

Pro parametr metody jazyka C# () použijte stejný název battlePlans :Use a matching name for the parameter of the C# method (battlePlans):

  • Pro Razor metodu obslužné rutiny stránky stránky s názvem Upload :For a Razor Pages page handler method named Upload:

    public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> battlePlans)
    
  • Pro metodu akce po kontroléru MVC:For an MVC POST controller action method:

    public async Task<IActionResult> Post(List<IFormFile> battlePlans)
    

Konfigurace serveru a aplikaceServer and app configuration

Omezení délky těla částíMultipart body length limit

MultipartBodyLengthLimit nastaví limit délky jednotlivých částí části.MultipartBodyLengthLimit sets the limit for the length of each multipart body. Oddíly formuláře, které překračují toto omezení, vyvolávají InvalidDataException při analýze.Form sections that exceed this limit throw an InvalidDataException when parsed. Výchozí hodnota je 134 217 728 (128 MB).The default is 134,217,728 (128 MB). Upravte limit pomocí MultipartBodyLengthLimit nastavení v Startup.ConfigureServices :Customize the limit using the MultipartBodyLengthLimit setting in Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<FormOptions>(options =>
    {
        // Set the limit to 256 MB
        options.MultipartBodyLengthLimit = 268435456;
    });
}

RequestFormLimitsAttribute slouží k nastavení MultipartBodyLengthLimit pro jednu stránku nebo akci.RequestFormLimitsAttribute is used to set the MultipartBodyLengthLimit for a single page or action.

V Razor aplikaci Pages použijte filtr s konvencí v nástroji Startup.ConfigureServices :In a Razor Pages app, apply the filter with a convention in Startup.ConfigureServices:

services.AddRazorPages(options =>
{
    options.Conventions
        .AddPageApplicationModelConvention("/FileUploadPage",
            model.Filters.Add(
                new RequestFormLimitsAttribute()
                {
                    // Set the limit to 256 MB
                    MultipartBodyLengthLimit = 268435456
                });
});

V Razor aplikaci Pages nebo aplikaci MVC použijte filtr na model stránky nebo metodu akce:In a Razor Pages app or an MVC app, apply the filter to the page model or action method:

// Set the limit to 256 MB
[RequestFormLimits(MultipartBodyLengthLimit = 268435456)]
public class BufferedSingleFileUploadPhysicalModel : PageModel
{
    ...
}

Kestrel maximální velikost textu požadavkuKestrel maximum request body size

Pro aplikace hostované v Kestrel je výchozí maximální velikost textu požadavku 30 000 000 bajtů, což je přibližně 28,6 MB.For apps hosted by Kestrel, the default maximum request body size is 30,000,000 bytes, which is approximately 28.6 MB. Přizpůsobte limit pomocí možnosti serveru MaxRequestBodySize Kestrel:Customize the limit using the MaxRequestBodySize Kestrel server option:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel((context, options) =>
            {
                // Handle requests up to 50 MB
                options.Limits.MaxRequestBodySize = 52428800;
            })
            .UseStartup<Startup>();
        });

RequestSizeLimitAttribute slouží k nastavení MaxRequestBodySize pro jednu stránku nebo akci.RequestSizeLimitAttribute is used to set the MaxRequestBodySize for a single page or action.

V Razor aplikaci Pages použijte filtr s konvencí v nástroji Startup.ConfigureServices :In a Razor Pages app, apply the filter with a convention in Startup.ConfigureServices:

services.AddRazorPages(options =>
{
    options.Conventions
        .AddPageApplicationModelConvention("/FileUploadPage",
            model =>
            {
                // Handle requests up to 50 MB
                model.Filters.Add(
                    new RequestSizeLimitAttribute(52428800));
            });
});

V Razor aplikaci Pages nebo aplikaci MVC použijte filtr na třídu obslužné rutiny stránky nebo na metodu akce:In a Razor pages app or an MVC app, apply the filter to the page handler class or action method:

// Handle requests up to 50 MB
[RequestSizeLimit(52428800)]
public class BufferedSingleFileUploadPhysicalModel : PageModel
{
    ...
}

RequestSizeLimitAttributeLze také použít pomocí @attribute Razor direktivy:The RequestSizeLimitAttribute can also be applied using the @attribute Razor directive:

@attribute [RequestSizeLimitAttribute(52428800)]

Další omezení KestrelOther Kestrel limits

Pro aplikace hostované v Kestrel se můžou vztahovat další omezení Kestrel:Other Kestrel limits may apply for apps hosted by Kestrel:

IISIIS

Výchozí limit počtu požadavků ( maxAllowedContentLength ) je 30 000 000 bajtů, což je přibližně 28,6 MB.The default request limit (maxAllowedContentLength) is 30,000,000 bytes, which is approximately 28.6 MB. Přizpůsobte limit v web.config souboru.Customize the limit in the web.config file. V následujícím příkladu je limit nastaven na 50 MB (52 428 800 bajtů):In the following example, the limit is set to 50 MB (52,428,800 bytes):

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="52428800" />
    </requestFiltering>
  </security>
</system.webServer>

maxAllowedContentLengthNastavení platí pouze pro službu IIS.The maxAllowedContentLength setting only applies to IIS. Další informace najdete v tématu omezení <requestLimits> požadavků .For more information, see Request Limits <requestLimits>.

Zvyšte maximální velikost textu požadavku pro požadavek HTTP nastavením IISServerOptions.MaxRequestBodySize v Startup.ConfigureServices .Increase the maximum request body size for the HTTP request by setting IISServerOptions.MaxRequestBodySize in Startup.ConfigureServices. V následujícím příkladu je limit nastaven na 50 MB (52 428 800 bajtů):In the following example, the limit is set to 50 MB (52,428,800 bytes):

services.Configure<IISServerOptions>(options =>
{
    options.MaxRequestBodySize = 52428800;
});

Další informace naleznete v tématu Hostování ASP.NET Core ve Windows se službou IIS.For more information, see Hostování ASP.NET Core ve Windows se službou IIS.

Řešení potížíTroubleshoot

Níže jsou uvedeny některé běžné problémy, které se vyskytly při práci s nahráváním souborů a jejich možnými řešeními.Below are some common problems encountered when working with uploading files and their possible solutions.

Při nasazení na server služby IIS se nenašla chyba.Not Found error when deployed to an IIS server

Následující chyba znamená, že nahraný soubor překračuje délku nakonfigurovaného obsahu serveru:The following error indicates that the uploaded file exceeds the server's configured content length:

HTTP 404.13 - Not Found
The request filtering module is configured to deny a request that exceeds the request content length.

Další informace najdete v části IIS .For more information, see the IIS section.

Chyba připojeníConnection failure

Chyba připojení a připojení k serveru pro resetování pravděpodobně znamená, že nahraný soubor překračuje maximální velikost textu požadavku Kestrel.A connection error and a reset server connection probably indicates that the uploaded file exceeds Kestrel's maximum request body size. Další informace najdete v části Kestrel maximální velikost textu požadavku .For more information, see the Kestrel maximum request body size section. Omezení připojení klientů Kestrel mohou také vyžadovat úpravu.Kestrel client connection limits may also require adjustment.

Výjimka odkazu s hodnotou null s IFormFileNull Reference Exception with IFormFile

Pokud kontroler přijímá odeslané soubory pomocí IFormFile , ale hodnota je null , potvrďte, že formulář HTML určuje enctype hodnotu multipart/form-data .If the controller is accepting uploaded files using IFormFile but the value is null, confirm that the HTML form is specifying an enctype value of multipart/form-data. Pokud tento atribut není nastaven na <form> elementu, odeslání souboru neproběhne a jakékoli vázané IFormFile argumenty jsou null .If this attribute isn't set on the <form> element, the file upload doesn't occur and any bound IFormFile arguments are null. Ujistěte se také, že nahrávání názvů v datech formuláře odpovídá pojmenování aplikace.Also confirm that the upload naming in form data matches the app's naming.

Proud je příliš dlouhý.Stream was too long

Příklady v tomto tématu MemoryStream se spoléhají na uchovávání obsahu nahraného souboru.The examples in this topic rely upon MemoryStream to hold the uploaded file's content. Omezení velikosti MemoryStream je int.MaxValue .The size limit of a MemoryStream is int.MaxValue. Pokud scénář nahrávání souborů aplikace vyžaduje, aby byl obsah souboru větší než 50 MB, použijte alternativní přístup, který nespoléhá na jednu z nich MemoryStream pro uchovávání obsahu nahraného souboru.If the app's file upload scenario requires holding file content larger than 50 MB, use an alternative approach that doesn't rely upon a single MemoryStream for holding an uploaded file's content.

ASP.NET Core podporuje nahrávání jednoho nebo více souborů pomocí vazby modelu ve vyrovnávací paměti pro menší soubory a streamování bez vyrovnávací paměti pro větší soubory.ASP.NET Core supports uploading one or more files using buffered model binding for smaller files and unbuffered streaming for larger files.

Zobrazit nebo stáhnout ukázkový kód (Jak stáhnout)View or download sample code (how to download)

Důležité informace o zabezpečeníSecurity considerations

Pokud chcete uživatelům poskytnout možnost nahrávat soubory na server, buďte opatrní.Use caution when providing users with the ability to upload files to a server. Útočníci se můžou pokusit:Attackers may attempt to:

  • Vykoná útok DOS (Denial of Service ).Execute denial of service attacks.
  • Nahrání virů nebo malwaruUpload viruses or malware.
  • Narušit sítě a servery jinými způsoby.Compromise networks and servers in other ways.

Bezpečnostní kroky, které snižují pravděpodobnost úspěšného útoku, jsou:Security steps that reduce the likelihood of a successful attack are:

  • Nahrajte soubory do vyhrazené oblasti pro nahrávání souborů, nejlépe do nesystémové jednotky.Upload files to a dedicated file upload area, preferably to a non-system drive. Vyhrazené umístění usnadňuje omezení zabezpečení pro nahrané soubory.A dedicated location makes it easier to impose security restrictions on uploaded files. Zakažte oprávnění EXECUTE pro umístění pro nahrání souboru.†Disable execute permissions on the file upload location.†
  • Neuchovávat nahrané soubory ve stejném stromu adresářů jako aplikace. notDo not persist uploaded files in the same directory tree as the app.†
  • Použijte název bezpečného souboru určený aplikací.Use a safe file name determined by the app. Nepoužívejte název souboru poskytnutý uživatelem nebo nedůvěryhodného názvu nahraného souboru. † HTML při zobrazení kódování názvu nedůvěryhodného souboru.Don't use a file name provided by the user or the untrusted file name of the uploaded file.† HTML encode the untrusted file name when displaying it. Například protokolování názvu souboru nebo zobrazení v uživatelském rozhraní ( Razor Automatické kódování HTML kódování).For example, logging the file name or displaying in UI (Razor automatically HTML encodes output).
  • Povolte pro specifikaci návrhu aplikace jenom schválené přípony souborů.†Allow only approved file extensions for the app's design specification.†
  • Ověřte, zda jsou na serveru provedeny kontroly na straně klienta. † Kontroly na straně klienta je snadné obejít.Verify that client-side checks are performed on the server.† Client-side checks are easy to circumvent.
  • Ověřte velikost nahraného souboru.Check the size of an uploaded file. Nastavte limit maximální velikosti, aby se zabránilo velkým nahrávání.†Set a maximum size limit to prevent large uploads.†
  • Pokud by soubory neměly být přepsány nahraným souborem se stejným názvem, před nahráním souboru ověřte název souboru proti databázi nebo fyzickému úložišti.When files shouldn't be overwritten by an uploaded file with the same name, check the file name against the database or physical storage before uploading the file.
  • Před uložením souboru spusťte v nahraném obsahu skener virů nebo malwaru.Run a virus/malware scanner on uploaded content before the file is stored.

†Ukázková aplikace předvádí přístup, který splňuje kritéria.†The sample app demonstrates an approach that meets the criteria.

Upozornění

Nahrávání škodlivého kódu do systému je často prvním krokem ke spuštění kódu, který může:Uploading malicious code to a system is frequently the first step to executing code that can:

  • Zcela získá kontrolu nad systémem.Completely gain control of a system.
  • Přetížit systém s výsledkem, že dojde k chybě systému.Overload a system with the result that the system crashes.
  • Napadnout data uživatelů nebo systémových dat.Compromise user or system data.
  • Použijte graffiti pro veřejné uživatelské rozhraní.Apply graffiti to a public UI.

Informace o omezení oblasti útoku při přijímání souborů uživateli najdete v následujících zdrojích informací:For information on reducing the attack surface area when accepting files from users, see the following resources:

Další informace o implementaci bezpečnostních opatření, včetně příkladů z ukázkové aplikace, najdete v části ověření .For more information on implementing security measures, including examples from the sample app, see the Validation section.

Scénáře úložištěStorage scenarios

Mezi běžné možnosti úložiště pro soubory patří:Common storage options for files include:

  • DatabázeDatabase

    • U malých nahrávání souborů je databáze často rychlejší než možnosti fyzického úložiště (systému souborů nebo síťového sdílení).For small file uploads, a database is often faster than physical storage (file system or network share) options.
    • Databáze je často pohodlnější než možnosti fyzického úložiště, protože načtení záznamu databáze pro uživatelská data může současně poskytovat obsah souboru (například obrázek miniatury).A database is often more convenient than physical storage options because retrieval of a database record for user data can concurrently supply the file content (for example, an avatar image).
    • Databáze je potenciálně levnější než použití služby úložiště dat.A database is potentially less expensive than using a data storage service.
  • Fyzické úložiště (systém souborů nebo síťová sdílená složka)Physical storage (file system or network share)

    • Pro nahrávání velkých souborů:For large file uploads:
      • Omezení databáze mohou omezit velikost nahrávání.Database limits may restrict the size of the upload.
      • Fyzické úložiště je často méně hospodárné než úložiště v databázi.Physical storage is often less economical than storage in a database.
    • Fyzické úložiště je potenciálně levnější než použití služby úložiště dat.Physical storage is potentially less expensive than using a data storage service.
    • Proces aplikace musí mít oprávnění ke čtení a zápisu do umístění úložiště.The app's process must have read and write permissions to the storage location. Nikdy neudělujte oprávnění EXECUTE.Never grant execute permission.
  • Služba úložiště dat (například Azure Blob Storage)Data storage service (for example, Azure Blob Storage)

    • Služby obvykle nabízejí vylepšenou škálovatelnost a odolnost proti místním řešením, které obvykle podléhají jednomu bodu selhání.Services usually offer improved scalability and resiliency over on-premises solutions that are usually subject to single points of failure.
    • Služby jsou potenciálně nižší náklady ve scénářích infrastruktury velkých úložišť.Services are potentially lower cost in large storage infrastructure scenarios.

    Další informace najdete v tématu rychlý Start: použití .NET k vytvoření objektu BLOB v úložišti objektů.For more information, see Quickstart: Use .NET to create a blob in object storage. Téma ukazuje UploadFromFileAsync , ale UploadFromStreamAsync dá se použít k uložení do FileStream úložiště objektů BLOB při práci s Stream .The topic demonstrates UploadFromFileAsync, but UploadFromStreamAsync can be used to save a FileStream to blob storage when working with a Stream.

Scénáře nahrávání souborůFile upload scenarios

Dva obecné přístupy k nahrávání souborů jsou ukládání do vyrovnávací paměti a streamování.Two general approaches for uploading files are buffering and streaming.

Do vyrovnávací pamětiBuffering

Celý soubor je načten do IFormFile , což je reprezentace souboru, který se používá ke zpracování nebo uložení souboru v jazyce C#.The entire file is read into an IFormFile, which is a C# representation of the file used to process or save the file.

Prostředky (disk, paměť) používané při nahrávání souborů závisí na počtu a velikosti souběžných nahrávání souborů.The resources (disk, memory) used by file uploads depend on the number and size of concurrent file uploads. Pokud se aplikace pokusí do vyrovnávací paměti příliš mnoho nahrávání, dojde k selhání lokality, když dojde k vynechání paměti nebo místa na disku.If an app attempts to buffer too many uploads, the site crashes when it runs out of memory or disk space. Pokud velikost nebo frekvence nahrávání souborů vyčerpá prostředky aplikace, použijte streamování.If the size or frequency of file uploads is exhausting app resources, use streaming.

Poznámka

Z paměti do dočasného souboru na disku se přesune libovolný soubor s vyrovnávací pamětí větší než 64 KB.Any single buffered file exceeding 64 KB is moved from memory to a temp file on disk.

Ukládání malých souborů do vyrovnávací paměti je popsáno v následujících částech tohoto tématu:Buffering small files is covered in the following sections of this topic:

StreamováníStreaming

Soubor se přijímá z požadavku na více částí a přímo se zpracovává nebo ukládá v aplikaci.The file is received from a multipart request and directly processed or saved by the app. Streamování nijak významně nezvyšuje výkon.Streaming doesn't improve performance significantly. Streamování snižuje nároky na paměť nebo místo na disku při nahrávání souborů.Streaming reduces the demands for memory or disk space when uploading files.

Streamování velkých souborů je zahrnuté v části nahrávání velkých souborů pomocí streamování .Streaming large files is covered in the Upload large files with streaming section.

Nahrávání malých souborů s vazbou modelu ve vyrovnávací paměti na fyzické úložištěUpload small files with buffered model binding to physical storage

Pro nahrání malých souborů použijte formulář s více částmi nebo sestavte požadavek POST pomocí JavaScriptu.To upload small files, use a multipart form or construct a POST request using JavaScript.

Následující příklad ukazuje použití Razor formuláře stránky k nahrání jednoho souboru (Pages/BufferedSingleFileUploadPhysical. cshtml do ukázkové aplikace):The following example demonstrates the use of a Razor Pages form to upload a single file (Pages/BufferedSingleFileUploadPhysical.cshtml in the sample app):

<form enctype="multipart/form-data" method="post">
    <dl>
        <dt>
            <label asp-for="FileUpload.FormFile"></label>
        </dt>
        <dd>
            <input asp-for="FileUpload.FormFile" type="file">
            <span asp-validation-for="FileUpload.FormFile"></span>
        </dd>
    </dl>
    <input asp-page-handler="Upload" class="btn" type="submit" value="Upload" />
</form>

Následující příklad je podobný předchozímu příkladu s tím rozdílem, že:The following example is analogous to the prior example except that:

  • K odeslání dat formuláře se používá JavaScript (Fetch API).JavaScript's (Fetch API) is used to submit the form's data.
  • Neexistuje žádné ověření.There's no validation.
<form action="BufferedSingleFileUploadPhysical/?handler=Upload" 
      enctype="multipart/form-data" onsubmit="AJAXSubmit(this);return false;" 
      method="post">
    <dl>
        <dt>
            <label for="FileUpload_FormFile">File</label>
        </dt>
        <dd>
            <input id="FileUpload_FormFile" type="file" 
                name="FileUpload.FormFile" />
        </dd>
    </dl>

    <input class="btn" type="submit" value="Upload" />

    <div style="margin-top:15px">
        <output name="result"></output>
    </div>
</form>

<script>
  async function AJAXSubmit (oFormElement) {
    var resultElement = oFormElement.elements.namedItem("result");
    const formData = new FormData(oFormElement);

    try {
    const response = await fetch(oFormElement.action, {
      method: 'POST',
      body: formData
    });

    if (response.ok) {
      window.location.href = '/';
    }

    resultElement.value = 'Result: ' + response.status + ' ' + 
      response.statusText;
    } catch (error) {
      console.error('Error:', error);
    }
  }
</script>

Chcete-li provést příspěvek formuláře v jazyce JavaScript pro klienty, kteří nepodporují rozhraní API pro načítání, použijte jeden z následujících přístupů:To perform the form POST in JavaScript for clients that don't support the Fetch API, use one of the following approaches:

  • Použijte načtenou výplň (například window. Fetch Fill (GitHub/Fetch)).Use a Fetch Polyfill (for example, window.fetch polyfill (github/fetch)).

  • Použijte XMLHttpRequest.Use XMLHttpRequest. Příklad:For example:

    <script>
      "use strict";
    
      function AJAXSubmit (oFormElement) {
        var oReq = new XMLHttpRequest();
        oReq.onload = function(e) { 
        oFormElement.elements.namedItem("result").value = 
          'Result: ' + this.status + ' ' + this.statusText;
        };
        oReq.open("post", oFormElement.action);
        oReq.send(new FormData(oFormElement));
      }
    </script>
    

Aby bylo možné podporovat nahrávání souborů, musí formuláře HTML určovat typ kódování ( enctype ) multipart/form-data .In order to support file uploads, HTML forms must specify an encoding type (enctype) of multipart/form-data.

Pro files vstupní element, který podporuje nahrávání více souborů, poskytněte multiple atribut <input> elementu:For a files input element to support uploading multiple files provide the multiple attribute on the <input> element:

<input asp-for="FileUpload.FormFiles" type="file" multiple>

Jednotlivé soubory nahrané na server jsou k dispozici prostřednictvím vazby modelu pomocí IFormFile .The individual files uploaded to the server can be accessed through Model Binding using IFormFile. Ukázková aplikace ukazuje více ukládání souborů do vyrovnávací paměti pro scénáře databáze a fyzických úložišť.The sample app demonstrates multiple buffered file uploads for database and physical storage scenarios.

Upozornění

Nepoužívejte not FileName vlastnost IFormFile jinou než pro zobrazení a protokolování.Do not use the FileName property of IFormFile other than for display and logging. Při zobrazení nebo protokolování je název souboru kódován HTML.When displaying or logging, HTML encode the file name. Útočník může poskytnout škodlivý název souboru, včetně úplných cest nebo relativních cest.An attacker can provide a malicious filename, including full paths or relative paths. Aplikace by měly:Applications should:

  • Odeberte cestu z názvu souboru zadaného uživatelem.Remove the path from the user-supplied filename.
  • Uložte název souboru s příponou PATH s kódováním HTML pro uživatelské rozhraní nebo protokolování.Save the HTML-encoded, path-removed filename for UI or logging.
  • Vygenerujte nový náhodný název souboru pro úložiště.Generate a new random filename for storage.

Následující kód odstraní cestu z názvu souboru:The following code removes the path from the file name:

string untrustedFileName = Path.GetFileName(pathName);

Zde uvedené příklady neberou ohled na zabezpečení.The examples provided thus far don't take into account security considerations. Další informace jsou k dispozici v následujících částech a ukázkové aplikaci:Additional information is provided by the following sections and the sample app:

Při nahrávání souborů pomocí vazby modelu a IFormFile může metoda Action přijmout:When uploading files using model binding and IFormFile, the action method can accept:

Poznámka

Vazba odpovídá souborům formuláře podle názvu.Binding matches form files by name. Například hodnota HTML v se name <input type="file" name="formFile"> musí shodovat s parametrem nebo vazbou vlastnosti jazyka C# ( FormFile ).For example, the HTML name value in <input type="file" name="formFile"> must match the C# parameter/property bound (FormFile). Další informace naleznete v části název atributu matched na název parametru metody post .For more information, see the Match name attribute value to parameter name of POST method section.

Následující příklad:The following example:

  • Projde jedním nebo více nahranými soubory.Loops through one or more uploaded files.
  • Pomocí Path. GetTempFileName vrátí úplnou cestu k souboru, včetně názvu souboru.Uses Path.GetTempFileName to return a full path for a file, including the file name.
  • Uloží soubory do místního systému souborů pomocí názvu souboru generovaného aplikací.Saves the files to the local file system using a file name generated by the app.
  • Vrátí celkový počet nahraných souborů a jejich velikost.Returns the total number and size of files uploaded.
public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> files)
{
    long size = files.Sum(f => f.Length);

    foreach (var formFile in files)
    {
        if (formFile.Length > 0)
        {
            var filePath = Path.GetTempFileName();

            using (var stream = System.IO.File.Create(filePath))
            {
                await formFile.CopyToAsync(stream);
            }
        }
    }

    // Process uploaded files
    // Don't rely on or trust the FileName property without validation.

    return Ok(new { count = files.Count, size });
}

Slouží Path.GetRandomFileName k vygenerování názvu souboru bez cesty.Use Path.GetRandomFileName to generate a file name without a path. V následujícím příkladu je cesta získána z konfigurace:In the following example, the path is obtained from configuration:

foreach (var formFile in files)
{
    if (formFile.Length > 0)
    {
        var filePath = Path.Combine(_config["StoredFilesPath"], 
            Path.GetRandomFileName());

        using (var stream = System.IO.File.Create(filePath))
        {
            await formFile.CopyToAsync(stream);
        }
    }
}

Cesta předaná do FileStream musí zahrnovat název souboru.The path passed to the FileStream must include the file name. Pokud není zadán název souboru, UnauthorizedAccessException je vyvolána za běhu.If the file name isn't provided, an UnauthorizedAccessException is thrown at runtime.

Soubory odeslané pomocí IFormFile techniky jsou v paměti nebo na disku na serveru před zpracováním uloženy do vyrovnávací paměti.Files uploaded using the IFormFile technique are buffered in memory or on disk on the server before processing. V rámci metody Action je IFormFile obsah přístupný jako Stream .Inside the action method, the IFormFile contents are accessible as a Stream. Kromě místního systému souborů je možné soubory ukládat do síťové sdílené složky nebo do služby úložiště souborů, jako je Azure Blob Storage.In addition to the local file system, files can be saved to a network share or to a file storage service, such as Azure Blob storage.

Další příklad, který projde několik souborů pro nahrání a používá bezpečné názvy souborů, najdete v ukázkové aplikaci v části Pages/BufferedMultipleFileUploadPhysical. cshtml. cs .For another example that loops over multiple files for upload and uses safe file names, see Pages/BufferedMultipleFileUploadPhysical.cshtml.cs in the sample app.

Upozornění

Cesta. GetTempFileName vyvolá výjimku, IOException Pokud jsou vytvořeny více než 65 535 souborů bez odstranění předchozích dočasných souborů.Path.GetTempFileName throws an IOException if more than 65,535 files are created without deleting previous temporary files. Limit 65 535 souborů je omezen na server.The limit of 65,535 files is a per-server limit. Další informace o tomto limitu pro operační systém Windows najdete v následujících tématech:For more information on this limit on Windows OS, see the remarks in the following topics:

Nahrávání malých souborů s vazbou modelu s vyrovnávací pamětí do databázeUpload small files with buffered model binding to a database

Chcete-li uložit data binárního souboru do databáze pomocí Entity Framework, definujte v Byte entitě vlastnost Array:To store binary file data in a database using Entity Framework, define a Byte array property on the entity:

public class AppFile
{
    public int Id { get; set; }
    public byte[] Content { get; set; }
}

Zadejte vlastnost modelu stránky pro třídu, která obsahuje IFormFile :Specify a page model property for the class that includes an IFormFile:

public class BufferedSingleFileUploadDbModel : PageModel
{
    ...

    [BindProperty]
    public BufferedSingleFileUploadDb FileUpload { get; set; }

    ...
}

public class BufferedSingleFileUploadDb
{
    [Required]
    [Display(Name="File")]
    public IFormFile FormFile { get; set; }
}

Poznámka

IFormFile lze ji použít přímo jako parametr metody akce nebo jako vlastnost vázaného modelu.IFormFile can be used directly as an action method parameter or as a bound model property. Předchozí příklad používá vlastnost vázaného modelu.The prior example uses a bound model property.

FileUploadJe použit ve Razor formuláři stránky:The FileUpload is used in the Razor Pages form:

<form enctype="multipart/form-data" method="post">
    <dl>
        <dt>
            <label asp-for="FileUpload.FormFile"></label>
        </dt>
        <dd>
            <input asp-for="FileUpload.FormFile" type="file">
        </dd>
    </dl>
    <input asp-page-handler="Upload" class="btn" type="submit" value="Upload">
</form>

Když je formulář publikovaný na serveru, zkopírujte IFormFile ho do datového proudu a uložte ho jako pole bajtů v databázi.When the form is POSTed to the server, copy the IFormFile to a stream and save it as a byte array in the database. V následujícím příkladu _dbContext ukládá kontext databáze aplikace:In the following example, _dbContext stores the app's database context:

public async Task<IActionResult> OnPostUploadAsync()
{
    using (var memoryStream = new MemoryStream())
    {
        await FileUpload.FormFile.CopyToAsync(memoryStream);

        // Upload the file if less than 2 MB
        if (memoryStream.Length < 2097152)
        {
            var file = new AppFile()
            {
                Content = memoryStream.ToArray()
            };

            _dbContext.File.Add(file);

            await _dbContext.SaveChangesAsync();
        }
        else
        {
            ModelState.AddModelError("File", "The file is too large.");
        }
    }

    return Page();
}

Předchozí příklad je podobný scénáři, který je znázorněný v ukázkové aplikaci:The preceding example is similar to a scenario demonstrated in the sample app:

  • Pages/BufferedSingleFileUploadDb. cshtmlPages/BufferedSingleFileUploadDb.cshtml
  • Pages/BufferedSingleFileUploadDb. cshtml. csPages/BufferedSingleFileUploadDb.cshtml.cs

Upozornění

Při ukládání binárních dat do relačních databází buďte opatrní, protože to může mít nepříznivý vliv na výkon.Use caution when storing binary data in relational databases, as it can adversely impact performance.

Nespoléhá se na nebo důvěřujete FileName vlastnosti IFormFile bez ověření.Don't rely on or trust the FileName property of IFormFile without validation. FileNameVlastnost by měla být použita pouze pro účely zobrazení a pouze po kódování HTML.The FileName property should only be used for display purposes and only after HTML encoding.

Uvedené příklady nevezmou ohled na zabezpečení.The examples provided don't take into account security considerations. Další informace jsou k dispozici v následujících částech a ukázkové aplikaci:Additional information is provided by the following sections and the sample app:

Nahrávání velkých souborů pomocí streamováníUpload large files with streaming

Následující příklad ukazuje, jak použít JavaScript ke streamování souboru do akce kontroleru.The following example demonstrates how to use JavaScript to stream a file to a controller action. Token proti padělání souboru se generuje pomocí vlastního atributu filtru a předává se do hlaviček protokolu HTTP klienta místo v textu žádosti.The file's antiforgery token is generated using a custom filter attribute and passed to the client HTTP headers instead of in the request body. Vzhledem k tomu, že metoda akce zpracovává nahraná data přímo, vazba modelu formuláře je zakázána jiným vlastním filtrem.Because the action method processes the uploaded data directly, form model binding is disabled by another custom filter. V rámci akce je obsah formuláře čten pomocí MultipartReader , který čte každou jednotlivou osobu MultipartSection , zpracovává soubor nebo ukládá obsah podle potřeby.Within the action, the form's contents are read using a MultipartReader, which reads each individual MultipartSection, processing the file or storing the contents as appropriate. Po načtení oddílů s více částmi provede akce vlastní vazbu modelu.After the multipart sections are read, the action performs its own model binding.

Počáteční odpověď stránky načte formulář a uloží token proti padělání do cookie (prostřednictvím GenerateAntiforgeryTokenCookieAttribute atributu).The initial page response loads the form and saves an antiforgery token in a cookie (via the GenerateAntiforgeryTokenCookieAttribute attribute). Atribut používá ASP.NET Core integrovanou podporu proti padělání pro nastavení cookie s tokenem požadavku:The attribute uses ASP.NET Core's built-in antiforgery support to set a cookie with a request token:

public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext context)
    {
        var antiforgery = context.HttpContext.RequestServices.GetService<IAntiforgery>();

        // Send the request token as a JavaScript-readable cookie
        var tokens = antiforgery.GetAndStoreTokens(context.HttpContext);

        context.HttpContext.Response.Cookies.Append(
            "RequestVerificationToken",
            tokens.RequestToken,
            new CookieOptions() { HttpOnly = false });
    }

    public override void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

DisableFormValueModelBindingAttributeSlouží k zakázání vazby modelu:The DisableFormValueModelBindingAttribute is used to disable model binding:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        var factories = context.ValueProviderFactories;
        factories.RemoveType<FormValueProviderFactory>();
        factories.RemoveType<JQueryFormValueProviderFactory>();
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

V ukázkové aplikaci a používá GenerateAntiforgeryTokenCookieAttribute se DisableFormValueModelBindingAttribute jako filtr pro modelové aplikace stránky /StreamedSingleFileUploadDb a /StreamedSingleFileUploadPhysical v Startup.ConfigureServices Razor konvencích použití stránek:In the sample app, GenerateAntiforgeryTokenCookieAttribute and DisableFormValueModelBindingAttribute are applied as filters to the page application models of /StreamedSingleFileUploadDb and /StreamedSingleFileUploadPhysical in Startup.ConfigureServices using Razor Pages conventions:

services.AddMvc()
    .AddRazorPagesOptions(options =>
        {
            options.Conventions
                .AddPageApplicationModelConvention("/StreamedSingleFileUploadDb",
                    model =>
                    {
                        model.Filters.Add(
                            new GenerateAntiforgeryTokenCookieAttribute());
                        model.Filters.Add(
                            new DisableFormValueModelBindingAttribute());
                    });
            options.Conventions
                .AddPageApplicationModelConvention("/StreamedSingleFileUploadPhysical",
                    model =>
                    {
                        model.Filters.Add(
                            new GenerateAntiforgeryTokenCookieAttribute());
                        model.Filters.Add(
                            new DisableFormValueModelBindingAttribute());
                    });
        })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Vzhledem k tomu, že vazba modelu nepřečte formulář, parametry, které jsou svázané z formuláře, se nezobrazují (budou pokračovat v práci s dotazem, trasou a hlavičkou).Since model binding doesn't read the form, parameters that are bound from the form don't bind (query, route, and header continue to work). Metoda Action pracuje přímo s Request vlastností.The action method works directly with the Request property. MultipartReaderSlouží ke čtení jednotlivých oddílů.A MultipartReader is used to read each section. Data klíč/hodnota jsou uložena v KeyValueAccumulator .Key/value data is stored in a KeyValueAccumulator. Po načtení oddílů s více částmi se obsah KeyValueAccumulator používá pro svázání dat formuláře s typem modelu.After the multipart sections are read, the contents of the KeyValueAccumulator are used to bind the form data to a model type.

Úplná StreamingController.UploadDatabase metoda pro streamování do databáze s EF Core:The complete StreamingController.UploadDatabase method for streaming to a database with EF Core:

[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadDatabase()
{
    if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
    {
        ModelState.AddModelError("File", 
            $"The request couldn't be processed (Error 1).");
        // Log error

        return BadRequest(ModelState);
    }

    // Accumulate the form data key-value pairs in the request (formAccumulator).
    var formAccumulator = new KeyValueAccumulator();
    var trustedFileNameForDisplay = string.Empty;
    var untrustedFileNameForStorage = string.Empty;
    var streamedFileContent = new byte[0];

    var boundary = MultipartRequestHelper.GetBoundary(
        MediaTypeHeaderValue.Parse(Request.ContentType),
        _defaultFormOptions.MultipartBoundaryLengthLimit);
    var reader = new MultipartReader(boundary, HttpContext.Request.Body);

    var section = await reader.ReadNextSectionAsync();

    while (section != null)
    {
        var hasContentDispositionHeader = 
            ContentDispositionHeaderValue.TryParse(
                section.ContentDisposition, out var contentDisposition);

        if (hasContentDispositionHeader)
        {
            if (MultipartRequestHelper
                .HasFileContentDisposition(contentDisposition))
            {
                untrustedFileNameForStorage = contentDisposition.FileName.Value;
                // Don't trust the file name sent by the client. To display
                // the file name, HTML-encode the value.
                trustedFileNameForDisplay = WebUtility.HtmlEncode(
                        contentDisposition.FileName.Value);

                streamedFileContent = 
                    await FileHelpers.ProcessStreamedFile(section, contentDisposition, 
                        ModelState, _permittedExtensions, _fileSizeLimit);

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }
            }
            else if (MultipartRequestHelper
                .HasFormDataContentDisposition(contentDisposition))
            {
                // Don't limit the key name length because the 
                // multipart headers length limit is already in effect.
                var key = HeaderUtilities
                    .RemoveQuotes(contentDisposition.Name).Value;
                var encoding = GetEncoding(section);

                if (encoding == null)
                {
                    ModelState.AddModelError("File", 
                        $"The request couldn't be processed (Error 2).");
                    // Log error

                    return BadRequest(ModelState);
                }

                using (var streamReader = new StreamReader(
                    section.Body,
                    encoding,
                    detectEncodingFromByteOrderMarks: true,
                    bufferSize: 1024,
                    leaveOpen: true))
                {
                    // The value length limit is enforced by 
                    // MultipartBodyLengthLimit
                    var value = await streamReader.ReadToEndAsync();

                    if (string.Equals(value, "undefined", 
                        StringComparison.OrdinalIgnoreCase))
                    {
                        value = string.Empty;
                    }

                    formAccumulator.Append(key, value);

                    if (formAccumulator.ValueCount > 
                        _defaultFormOptions.ValueCountLimit)
                    {
                        // Form key count limit of 
                        // _defaultFormOptions.ValueCountLimit 
                        // is exceeded.
                        ModelState.AddModelError("File", 
                            $"The request couldn't be processed (Error 3).");
                        // Log error

                        return BadRequest(ModelState);
                    }
                }
            }
        }

        // Drain any remaining section body that hasn't been consumed and
        // read the headers for the next section.
        section = await reader.ReadNextSectionAsync();
    }

    // Bind form data to the model
    var formData = new FormData();
    var formValueProvider = new FormValueProvider(
        BindingSource.Form,
        new FormCollection(formAccumulator.GetResults()),
        CultureInfo.CurrentCulture);
    var bindingSuccessful = await TryUpdateModelAsync(formData, prefix: "",
        valueProvider: formValueProvider);

    if (!bindingSuccessful)
    {
        ModelState.AddModelError("File", 
            "The request couldn't be processed (Error 5).");
        // Log error

        return BadRequest(ModelState);
    }

    // **WARNING!**
    // In the following example, the file is saved without
    // scanning the file's contents. In most production
    // scenarios, an anti-virus/anti-malware scanner API
    // is used on the file before making the file available
    // for download or for use by other systems. 
    // For more information, see the topic that accompanies 
    // this sample app.

    var file = new AppFile()
    {
        Content = streamedFileContent,
        UntrustedName = untrustedFileNameForStorage,
        Note = formData.Note,
        Size = streamedFileContent.Length, 
        UploadDT = DateTime.UtcNow
    };

    _context.File.Add(file);
    await _context.SaveChangesAsync();

    return Created(nameof(StreamingController), null);
}

MultipartRequestHelper (Nástroje/MultipartRequestHelper. cs):MultipartRequestHelper (Utilities/MultipartRequestHelper.cs):

using System;
using System.IO;
using Microsoft.Net.Http.Headers;

namespace SampleApp.Utilities
{
    public static class MultipartRequestHelper
    {
        // Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
        // The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit.
        public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
        {
            var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;

            if (string.IsNullOrWhiteSpace(boundary))
            {
                throw new InvalidDataException("Missing content-type boundary.");
            }

            if (boundary.Length > lengthLimit)
            {
                throw new InvalidDataException(
                    $"Multipart boundary length limit {lengthLimit} exceeded.");
            }

            return boundary;
        }

        public static bool IsMultipartContentType(string contentType)
        {
            return !string.IsNullOrEmpty(contentType)
                   && contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
        }

        public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
        {
            // Content-Disposition: form-data; name="key";
            return contentDisposition != null
                && contentDisposition.DispositionType.Equals("form-data")
                && string.IsNullOrEmpty(contentDisposition.FileName.Value)
                && string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
        }

        public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
        {
            // Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
            return contentDisposition != null
                && contentDisposition.DispositionType.Equals("form-data")
                && (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
                    || !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
        }
    }
}

Úplná StreamingController.UploadPhysical metoda pro streamování do fyzického umístění:The complete StreamingController.UploadPhysical method for streaming to a physical location:

[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadPhysical()
{
    if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
    {
        ModelState.AddModelError("File", 
            $"The request couldn't be processed (Error 1).");
        // Log error

        return BadRequest(ModelState);
    }

    var boundary = MultipartRequestHelper.GetBoundary(
        MediaTypeHeaderValue.Parse(Request.ContentType),
        _defaultFormOptions.MultipartBoundaryLengthLimit);
    var reader = new MultipartReader(boundary, HttpContext.Request.Body);
    var section = await reader.ReadNextSectionAsync();

    while (section != null)
    {
        var hasContentDispositionHeader = 
            ContentDispositionHeaderValue.TryParse(
                section.ContentDisposition, out var contentDisposition);

        if (hasContentDispositionHeader)
        {
            // This check assumes that there's a file
            // present without form data. If form data
            // is present, this method immediately fails
            // and returns the model error.
            if (!MultipartRequestHelper
                .HasFileContentDisposition(contentDisposition))
            {
                ModelState.AddModelError("File", 
                    $"The request couldn't be processed (Error 2).");
                // Log error

                return BadRequest(ModelState);
            }
            else
            {
                // Don't trust the file name sent by the client. To display
                // the file name, HTML-encode the value.
                var trustedFileNameForDisplay = WebUtility.HtmlEncode(
                        contentDisposition.FileName.Value);
                var trustedFileNameForFileStorage = Path.GetRandomFileName();

                // **WARNING!**
                // In the following example, the file is saved without
                // scanning the file's contents. In most production
                // scenarios, an anti-virus/anti-malware scanner API
                // is used on the file before making the file available
                // for download or for use by other systems. 
                // For more information, see the topic that accompanies 
                // this sample.

                var streamedFileContent = await FileHelpers.ProcessStreamedFile(
                    section, contentDisposition, ModelState, 
                    _permittedExtensions, _fileSizeLimit);

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }

                using (var targetStream = System.IO.File.Create(
                    Path.Combine(_targetFilePath, trustedFileNameForFileStorage)))
                {
                    await targetStream.WriteAsync(streamedFileContent);

                    _logger.LogInformation(
                        "Uploaded file '{TrustedFileNameForDisplay}' saved to " +
                        "'{TargetFilePath}' as {TrustedFileNameForFileStorage}", 
                        trustedFileNameForDisplay, _targetFilePath, 
                        trustedFileNameForFileStorage);
                }
            }
        }

        // Drain any remaining section body that hasn't been consumed and
        // read the headers for the next section.
        section = await reader.ReadNextSectionAsync();
    }

    return Created(nameof(StreamingController), null);
}

V ukázkové aplikaci jsou kontroly ověřování zpracovávány nástrojem FileHelpers.ProcessStreamedFile .In the sample app, validation checks are handled by FileHelpers.ProcessStreamedFile.

OvěřováníValidation

Třída ukázkové aplikace FileHelpers ukazuje několik kontrol ukládání souborů do vyrovnávací paměti IFormFile a datových proudů při nahrávání.The sample app's FileHelpers class demonstrates a several checks for buffered IFormFile and streamed file uploads. Informace o zpracování IFormFile ukládání souborů do vyrovnávací paměti v ukázkové aplikaci naleznete v ProcessFormFile metodě v souboru Utilities/Helper. cs .For processing IFormFile buffered file uploads in the sample app, see the ProcessFormFile method in the Utilities/FileHelpers.cs file. Pro zpracování streamované soubory se podívejte na ProcessStreamedFile metodu ve stejném souboru.For processing streamed files, see the ProcessStreamedFile method in the same file.

Upozornění

Metody zpracování ověřování, které jsou znázorněné v ukázkové aplikaci, nekontrolují obsah nahraných souborů.The validation processing methods demonstrated in the sample app don't scan the content of uploaded files. Ve většině produkčních scénářů se v souboru používá rozhraní API pro skenování virů nebo malwaru, než je soubor dostupný uživatelům nebo jiným systémům.In most production scenarios, a virus/malware scanner API is used on the file before making the file available to users or other systems.

I když ukázka tématu poskytuje pracovní příklad technik ověřování, Neimplementujte FileHelpers třídu v produkční aplikaci, pokud:Although the topic sample provides a working example of validation techniques, don't implement the FileHelpers class in a production app unless you:

  • Plně rozumíte implementaci.Fully understand the implementation.
  • Upravte implementaci podle potřeby pro prostředí a specifikace aplikace.Modify the implementation as appropriate for the app's environment and specifications.

Nikdy nepoužívejte nerozlišený kód zabezpečení v aplikaci bez nutnosti řešit tyto požadavky.Never indiscriminately implement security code in an app without addressing these requirements.

Ověření obsahuContent validation

Pro nahraný obsah použijte rozhraní API pro kontrolu virů a malwaru třetí strany.Use a third party virus/malware scanning API on uploaded content.

Prohledávání souborů je náročné na prostředky serveru ve scénářích s vysokým objemem.Scanning files is demanding on server resources in high volume scenarios. Pokud dojde ke snížení výkonu zpracování požadavků z důvodu kontroly souborů, zvažte přesměrování práce skenování na službu na pozadí, případně služby spuštěné na serveru, který se liší od serveru aplikace.If request processing performance is diminished due to file scanning, consider offloading the scanning work to a background service, possibly a service running on a server different from the app's server. Nahrané soubory se obvykle uchovávají v oblasti v karanténě, dokud je kontrola virů na pozadí nevrátí.Typically, uploaded files are held in a quarantined area until the background virus scanner checks them. Když soubor projde, soubor se přesune do normálního umístění úložiště souborů.When a file passes, the file is moved to the normal file storage location. Tyto kroky se obvykle provádí ve spojení s databázovým záznamem, který indikuje stav kontroly souboru.These steps are usually performed in conjunction with a database record that indicates the scanning status of a file. Při použití takového přístupu zůstane aplikace a Server App zaměřené na reakci na požadavky.By using such an approach, the app and app server remain focused on responding to requests.

Ověření přípony souboruFile extension validation

Přípona nahraného souboru by měla být zaškrtnutá na seznamu povolených rozšíření.The uploaded file's extension should be checked against a list of permitted extensions. Příklad:For example:

private string[] permittedExtensions = { ".txt", ".pdf" };

var ext = Path.GetExtension(uploadedFileName).ToLowerInvariant();

if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
{
    // The extension is invalid ... discontinue processing the file
}

Ověření podpisu souboruFile signature validation

Podpis souboru se určuje na prvních několika bajtech na začátku souboru.A file's signature is determined by the first few bytes at the start of a file. Tyto bajty lze použít k určení, zda přípona odpovídá obsahu souboru.These bytes can be used to indicate if the extension matches the content of the file. Ukázková aplikace zkontroluje podpisy souborů pro několik běžných typů souborů.The sample app checks file signatures for a few common file types. V následujícím příkladu se pro tento soubor kontroluje podpis souboru obrázku JPEG:In the following example, the file signature for a JPEG image is checked against the file:

private static readonly Dictionary<string, List<byte[]>> _fileSignature = 
    new Dictionary<string, List<byte[]>>
{
    { ".jpeg", new List<byte[]>
        {
            new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
            new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
            new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 },
        }
    },
};

using (var reader = new BinaryReader(uploadedFileData))
{
    var signatures = _fileSignature[ext];
    var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));

    return signatures.Any(signature => 
        headerBytes.Take(signature.Length).SequenceEqual(signature));
}

Další signatury souborů získáte v dokumentaci signatury souborů a oficiálních souborů.To obtain additional file signatures, see the File Signatures Database and official file specifications.

Zabezpečení názvu souboruFile name security

Nikdy nepoužívejte název souboru dodaný klientem pro uložení souboru do fyzického úložiště.Never use a client-supplied file name for saving a file to physical storage. Vytvořte bezpečný název souboru pro soubor pomocí cesty. GetRandomFileName nebo cesty. GetTempFileName a vytvořte úplnou cestu (včetně názvu souboru) pro dočasné úložiště.Create a safe file name for the file using Path.GetRandomFileName or Path.GetTempFileName to create a full path (including the file name) for temporary storage.

Razor Automatické kódování HTML kóduje hodnoty vlastností pro zobrazení.Razor automatically HTML encodes property values for display. Následující kód je bezpečné použít:The following code is safe to use:

@foreach (var file in Model.DatabaseFiles) {
    <tr>
        <td>
            @file.UntrustedName
        </td>
    </tr>
}

Mimo Razor , vždy HtmlEncode obsah názvu souboru z požadavku uživatele.Outside of Razor, always HtmlEncode file name content from a user's request.

Mnoho implementací musí zahrnovat kontrolu, že soubor existuje. v opačném případě je soubor přepsán souborem se stejným názvem.Many implementations must include a check that the file exists; otherwise, the file is overwritten by a file of the same name. Poskytněte další logiku pro splnění specifikací vaší aplikace.Supply additional logic to meet your app's specifications.

Ověřování velikostiSize validation

Omezte velikost nahraných souborů.Limit the size of uploaded files.

V ukázkové aplikaci je velikost souboru omezená na 2 MB (uvedené v bajtech).In the sample app, the size of the file is limited to 2 MB (indicated in bytes). Limit se poskytuje prostřednictvím Konfigurace z appsettings.jsv souboru:The limit is supplied via Configuration from the appsettings.json file:

{
  "FileSizeLimit": 2097152
}

FileSizeLimitTřída je vložena do PageModel tříd:The FileSizeLimit is injected into PageModel classes:

public class BufferedSingleFileUploadPhysicalModel : PageModel
{
    private readonly long _fileSizeLimit;

    public BufferedSingleFileUploadPhysicalModel(IConfiguration config)
    {
        _fileSizeLimit = config.GetValue<long>("FileSizeLimit");
    }

    ...
}

Když velikost souboru překročí limit, soubor se odmítne:When a file size exceeds the limit, the file is rejected:

if (formFile.Length > _fileSizeLimit)
{
    // The file is too large ... discontinue processing the file
}

Porovnává hodnotu atributu name s parametrem název metody POSTMatch name attribute value to parameter name of POST method

V Razor neformulářích, které publikují data formuláře nebo přímo využívají JavaScript FormData , název zadaný v prvku formuláře nebo FormData musí odpovídat názvu parametru v akci kontroleru.In non-Razor forms that POST form data or use JavaScript's FormData directly, the name specified in the form's element or FormData must match the name of the parameter in the controller's action.

V následujícím příkladu:In the following example:

  • Při použití <input> elementu name je atribut nastaven na hodnotu battlePlans :When using an <input> element, the name attribute is set to the value battlePlans:

    <input type="file" name="battlePlans" multiple>
    
  • Při použití FormData v jazyce JavaScript je název nastaven na hodnotu battlePlans :When using FormData in JavaScript, the name is set to the value battlePlans:

    var formData = new FormData();
    
    for (var file in files) {
      formData.append("battlePlans", file, file.name);
    }
    

Pro parametr metody jazyka C# () použijte stejný název battlePlans :Use a matching name for the parameter of the C# method (battlePlans):

  • Pro Razor metodu obslužné rutiny stránky stránky s názvem Upload :For a Razor Pages page handler method named Upload:

    public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> battlePlans)
    
  • Pro metodu akce po kontroléru MVC:For an MVC POST controller action method:

    public async Task<IActionResult> Post(List<IFormFile> battlePlans)
    

Konfigurace serveru a aplikaceServer and app configuration

Omezení délky těla částíMultipart body length limit

MultipartBodyLengthLimit nastaví limit délky jednotlivých částí části.MultipartBodyLengthLimit sets the limit for the length of each multipart body. Oddíly formuláře, které překračují toto omezení, vyvolávají InvalidDataException při analýze.Form sections that exceed this limit throw an InvalidDataException when parsed. Výchozí hodnota je 134 217 728 (128 MB).The default is 134,217,728 (128 MB). Upravte limit pomocí MultipartBodyLengthLimit nastavení v Startup.ConfigureServices :Customize the limit using the MultipartBodyLengthLimit setting in Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<FormOptions>(options =>
    {
        // Set the limit to 256 MB
        options.MultipartBodyLengthLimit = 268435456;
    });
}

RequestFormLimitsAttribute slouží k nastavení MultipartBodyLengthLimit pro jednu stránku nebo akci.RequestFormLimitsAttribute is used to set the MultipartBodyLengthLimit for a single page or action.

V Razor aplikaci Pages použijte filtr s konvencí v nástroji Startup.ConfigureServices :In a Razor Pages app, apply the filter with a convention in Startup.ConfigureServices:

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        options.Conventions
            .AddPageApplicationModelConvention("/FileUploadPage",
                model.Filters.Add(
                    new RequestFormLimitsAttribute()
                    {
                        // Set the limit to 256 MB
                        MultipartBodyLengthLimit = 268435456
                    });
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

V Razor aplikaci Pages nebo aplikaci MVC použijte filtr na model stránky nebo metodu akce:In a Razor Pages app or an MVC app, apply the filter to the page model or action method:

// Set the limit to 256 MB
[RequestFormLimits(MultipartBodyLengthLimit = 268435456)]
public class BufferedSingleFileUploadPhysicalModel : PageModel
{
    ...
}

Kestrel maximální velikost textu požadavkuKestrel maximum request body size

Pro aplikace hostované v Kestrel je výchozí maximální velikost textu požadavku 30 000 000 bajtů, což je přibližně 28,6 MB.For apps hosted by Kestrel, the default maximum request body size is 30,000,000 bytes, which is approximately 28.6 MB. Přizpůsobte limit pomocí možnosti serveru MaxRequestBodySize Kestrel:Customize the limit using the MaxRequestBodySize Kestrel server option:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, options) =>
        {
            // Handle requests up to 50 MB
            options.Limits.MaxRequestBodySize = 52428800;
        });

RequestSizeLimitAttribute slouží k nastavení MaxRequestBodySize pro jednu stránku nebo akci.RequestSizeLimitAttribute is used to set the MaxRequestBodySize for a single page or action.

V Razor aplikaci Pages použijte filtr s konvencí v nástroji Startup.ConfigureServices :In a Razor Pages app, apply the filter with a convention in Startup.ConfigureServices:

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        options.Conventions
            .AddPageApplicationModelConvention("/FileUploadPage",
                model =>
                {
                    // Handle requests up to 50 MB
                    model.Filters.Add(
                        new RequestSizeLimitAttribute(52428800));
                });
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

V Razor aplikaci Pages nebo aplikaci MVC použijte filtr na třídu obslužné rutiny stránky nebo na metodu akce:In a Razor pages app or an MVC app, apply the filter to the page handler class or action method:

// Handle requests up to 50 MB
[RequestSizeLimit(52428800)]
public class BufferedSingleFileUploadPhysicalModel : PageModel
{
    ...
}

Další omezení KestrelOther Kestrel limits

Pro aplikace hostované v Kestrel se můžou vztahovat další omezení Kestrel:Other Kestrel limits may apply for apps hosted by Kestrel:

IISIIS

Výchozí limit počtu požadavků ( maxAllowedContentLength ) je 30 000 000 bajtů, což je přibližně 28,6 MB.The default request limit (maxAllowedContentLength) is 30,000,000 bytes, which is approximately 28.6 MB. Přizpůsobte limit v web.config souboru.Customize the limit in the web.config file. V následujícím příkladu je limit nastaven na 50 MB (52 428 800 bajtů):In the following example, the limit is set to 50 MB (52,428,800 bytes):

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="52428800" />
    </requestFiltering>
  </security>
</system.webServer>

maxAllowedContentLengthNastavení platí pouze pro službu IIS.The maxAllowedContentLength setting only applies to IIS. Další informace najdete v tématu omezení <requestLimits> požadavků .For more information, see Request Limits <requestLimits>.

Zvyšte maximální velikost textu požadavku pro požadavek HTTP nastavením IISServerOptions.MaxRequestBodySize v Startup.ConfigureServices .Increase the maximum request body size for the HTTP request by setting IISServerOptions.MaxRequestBodySize in Startup.ConfigureServices. V následujícím příkladu je limit nastaven na 50 MB (52 428 800 bajtů):In the following example, the limit is set to 50 MB (52,428,800 bytes):

services.Configure<IISServerOptions>(options =>
{
    options.MaxRequestBodySize = 52428800;
});

Další informace naleznete v tématu Hostování ASP.NET Core ve Windows se službou IIS.For more information, see Hostování ASP.NET Core ve Windows se službou IIS.

Řešení potížíTroubleshoot

Níže jsou uvedeny některé běžné problémy, které se vyskytly při práci s nahráváním souborů a jejich možnými řešeními.Below are some common problems encountered when working with uploading files and their possible solutions.

Při nasazení na server služby IIS se nenašla chyba.Not Found error when deployed to an IIS server

Následující chyba znamená, že nahraný soubor překračuje délku nakonfigurovaného obsahu serveru:The following error indicates that the uploaded file exceeds the server's configured content length:

HTTP 404.13 - Not Found
The request filtering module is configured to deny a request that exceeds the request content length.

Další informace najdete v části IIS .For more information, see the IIS section.

Chyba připojeníConnection failure

Chyba připojení a připojení k serveru pro resetování pravděpodobně znamená, že nahraný soubor překračuje maximální velikost textu požadavku Kestrel.A connection error and a reset server connection probably indicates that the uploaded file exceeds Kestrel's maximum request body size. Další informace najdete v části Kestrel maximální velikost textu požadavku .For more information, see the Kestrel maximum request body size section. Omezení připojení klientů Kestrel mohou také vyžadovat úpravu.Kestrel client connection limits may also require adjustment.

Výjimka odkazu s hodnotou null s IFormFileNull Reference Exception with IFormFile

Pokud kontroler přijímá odeslané soubory pomocí IFormFile , ale hodnota je null , potvrďte, že formulář HTML určuje enctype hodnotu multipart/form-data .If the controller is accepting uploaded files using IFormFile but the value is null, confirm that the HTML form is specifying an enctype value of multipart/form-data. Pokud tento atribut není nastaven na <form> elementu, odeslání souboru neproběhne a jakékoli vázané IFormFile argumenty jsou null .If this attribute isn't set on the <form> element, the file upload doesn't occur and any bound IFormFile arguments are null. Ujistěte se také, že nahrávání názvů v datech formuláře odpovídá pojmenování aplikace.Also confirm that the upload naming in form data matches the app's naming.

Proud je příliš dlouhý.Stream was too long

Příklady v tomto tématu MemoryStream se spoléhají na uchovávání obsahu nahraného souboru.The examples in this topic rely upon MemoryStream to hold the uploaded file's content. Omezení velikosti MemoryStream je int.MaxValue .The size limit of a MemoryStream is int.MaxValue. Pokud scénář nahrávání souborů aplikace vyžaduje, aby byl obsah souboru větší než 50 MB, použijte alternativní přístup, který nespoléhá na jednu z nich MemoryStream pro uchovávání obsahu nahraného souboru.If the app's file upload scenario requires holding file content larger than 50 MB, use an alternative approach that doesn't rely upon a single MemoryStream for holding an uploaded file's content.

Další zdrojeAdditional resources