С файлами меньшего размера (например, 30 МБ или около того) у меня нет проблем. Однако как только я пытаюсь загрузить файл размером 89 МБ в свой контроллер API, он выдает ошибки (Request Entity Too Large)
:
Из моего интерфейса:
POST http://localhost:21971/api/import/MediaUpload?sessionID=***&pid=*** net::ERR_FAILED 413 (Request Entity Too Large)
Мой серверный проект API .Net 8 содержит файл import.controller.cs:
[HttpPost]
[Route("MediaUpload")]
public async Task<IActionResult> MediaUpload([FromQuery] string sessionId, [FromQuery] string pid, [FromForm] ImportMediaUploadModel formData)
{
IFormCollection form;
try
{
form = await Request.ReadFormAsync();
form.Keys.ToList().ForEach(key =>
{
if (key.Equals("MediaInfo", StringComparison.InvariantCultureIgnoreCase))
{
formData.MediaInfo = JsonConvert.DeserializeObject<List<MediaInfo>>(form[key]);
}
});
}
for (var i = 0; i < formData.Files?.Count; i++)
{
UploadFile(formData.Files[i]);
}
// ...
return Ok();
}
private async void UploadFile(IFormFile file)
{
var thisFileName = file.FileName.Trim('\"');
string? localPath = config[key: "ImportImageFolderPath"];
string fullPath = Path.Combine(localPath, thisFileName);
FileInfo fileInfo = new(fullPath);
using var stream = System.IO.File.Create(fullPath);
await file.CopyToAsync(stream);
}
Я получаю ту же ошибку при загрузке в среду контроля качества (а не только на локальном хосте).
Я читаю некоторые документы, такие как эта, https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-8.0, но мне не хватает опыта -уровень знаний в последней версии .Net.
Заранее благодарим за любые советы о том, как справиться с ограничениями на загрузку файлов в .Net 8.
В нашем html-шаблоне две соответствующие части находятся здесь:
<input
multiple
#fileInputElement
type = "file"
style = "display:none;"
(change) = "loadImagesFromInput($event)"
accept = ".jpg, .tiff, .png, .gif, .bmp, .pdf, .avi, .wmv, .mp4, .mpg, .mpeg, .mov, .3gp, .3g2, .m4v, .fda"
/>
<button mat-flat-button color = "primary" (click) = "openFileBrowser()" title = "Add Images" i18n-title = "@@addImages">
<mat-icon svgIcon = "add"></mat-icon>
<span i18n = "@@addImages">Add Images</span>
</button>
<span class = "spacer"></span>
<button mat-flat-button color = "primary" [disabled] = "isSaveButtonDisabled()" (click) = "save()" title = "Save"
i18n-title = "@@save">
<mat-icon svgIcon = "save-to-disk"></mat-icon>
<span i18n = "Button|Generic label for save option@@save">Save</span>
</button>
и наша машинопись:
save(): void {
this.isLoading = true;
const sessId = this.appSession.session.SessionID;
const params = { pID: this.selectedPat.PID };
// Populate MediaInfo object (some conversions to match the server model)
const mediaInfo: MediaInfo[] = this.importImages.map((image) => {
return {
PID: this.selectedPat.PID,
/// add'l fields removed for brevity..
};
});
const submitFormData = () => {
const formData = new FormData();
formData.append('MediaInfo', JSON.stringify(mediaInfo));
formData.append('CsiXmlFolder', this.csiXmlFolder);
for (let i = 0; i < this.importImages.length; i++) {
if (this.importImages[i].image.date !== '' && this.importImages[i].image.date !== undefined) {
this.importImages[i].image.date = formatDate(this.importImages[i].image.date, 'shortDate', 'en-US');
}
}
for (const image of this.importImages) {
formData.append('files', this.dataURItoBlob(image.image.TnUrl), image.image.name);
}
const captureEndpoint = this.selectedDeviceObj.Url;
this.importService.saveImportObjects(formData, params, captureEndpoint).subscribe({
next: (data) => {
// toast msg, etc.
},
});
};
saveImportObjects(formData: FormData, params, endpointUri: string): Observable<HttpClient> {
// NOTE: multipart and boundary header added by browser automatically.
let url = '';
let pid = '';
endpointUri = "http://localhost:21971/api/import/MediaUpload?"; // ** LOCAL DEV TESTING **
if (endpointUri !== null) {
url = `${endpointUri}sessID=${this.appSession.getSess()}&pId=${pid}`;
}
return this.http.post<any>(url, formData);
}
Также немного проще просто создать класс со всеми необходимыми полями... одно из них — IFormFile, а затем [привязать] его к POST. (вместо того, чтобы смешивать параметры запроса, подобные get, и POST, а затем получать необработанный запрос, повторять поля формы, десериализовать и т. д. Привязка .net выполнит всю работу за вас)
интерфейс немного слишком сложен. Вам нужно просто получить форму и создать из нее FormData... например: let form = document.getElementById('YourFormsID')! как HTMLFormElement; пусть formData = новый FormData (форма); Затем позвольте ASP.NET привязаться к форме. Прямо сейчас похоже, что вы создаете копию на стороне клиента, а затем отправляете ее... (дважды? как mediaInfo, а затем как файлы?) Все поля могут быть привязаны к их именам, файлы могут быть привязаны к List< IFormFile>ex: [BindProperty] public List<IFormFile> fileInputElement {get; набор; } (вы привяжете атрибут name)
Вы можете использовать атрибут [RequestSizeLimit(500*1024*1024)]
для обработки запросов размером до 500 МБ.
[HttpPost]
[Route("MediaUpload")]
[RequestSizeLimit(500*1024*1024)]
public async Task<IActionResult> MediaUpload([FromQuery] string sessionId, [FromQuery] string pid, [FromForm] ImportMediaUploadModel formData)
{
Или вы можете настроить в Program.cs, как показано ниже:
builder.Services.Configure<FormOptions>(x => {
//x.ValueCountLimit = int.MaxValue;
x.MultipartBodyLengthLimit = long.MaxValue;
});
Третий способ — добавить файл web.config и использовать атрибут [DisableRequestSizeLimit]
, см. этот ответ.
RequestSizeLimit, похоже, не работает на уровне контроллера. Размер моего файла составляет 89 МБ, но он по-прежнему выдает ошибку «Слишком большой» во внешнем интерфейсе. Я даже ни разу не достиг точки останова на сервере (которая всегда работает для моих небольших загрузок файлов размером 30 МБ). Попробую идею с инъекцией строителя..
не работает на уровне program.cs ни с конфигурацией builder.Services - FormOptions, ни с IISServerOptions. Для больших файлов он просто останавливается в браузере; никогда не попадает в мой серверный экземпляр vs2022, работающий на локальном хосте.
вам, вероятно, следует включить свой внешний код в свой вопрос вместе с кодом для класса ImportMediaUploadModel.
Привет @bob.mazzo, как сказал браузер, пожалуйста, поделитесь своим кодом внешнего интерфейса.
Привет @browsermator. Я опубликовал два соответствующих метода TS интерфейса, спасибо.
на уровне IIS нам нужно было разобраться с опцией «Фильтр запросов» на веб-сайте. Я мог бы сделать это в web.config. Все это в этом посте inflectra.com/Support/KnowledgeBase/KB306.aspx .
В качестве примечания: ничто из того, что я делал на уровне API C#, не пропускало бы объем, превышающий 30 МБ, на моем локальном компьютере.
Конфигурация IIS, необходимая для увеличения максимального размера файла: https://www.inflectra.com/Support/KnowledgeBase/KB306.aspx.
В веб.конфигурации:
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength = "2147483648" />
</requestFiltering>
</security>
</system.webServer>
или
Прямо в настройках IIS - редактировать Фильтрация запросов.
Ответ @Rena выше также содержит следующую ссылку: web.config Как увеличить или установить неограниченный размер загружаемого медиафайла в проекте ASP.NET Core 2.2
Кажется, то же самое, что я поделился в ссылке третьего способа. Так это решение?
@Rena - я был слишком сосредоточен на части C# API и, вероятно, пропустил часть web.config. Кроме того, я понял, что мы НЕ можем больше использовать web.config в нашем проекте .Net 8 (а скорее только appsettings.json); но я не осознавал, что когда мы публикуем/создаем проекты, действительно создается файл web.config.
просто примечание по безопасности: вам следует избегать этого: var thisFileName = file.FileName.Trim('\"'); а затем использовать это для создания файла. Никогда не доверяйте пользовательскому вводу. Вы должны полностью контролировать имя файла и расширение, используемое при сохранении файла на вашем сервере (расширение случаев/переключателя, если вам нужно... разрешить только определенные случаи.. используйте резервное расширение, если у вас нет для этого дела).