Как прочитать IFormFile

Рейтинг: 0Ответов: 1Опубликовано: 23.08.2023

Как прочитать файл IFormFile не прибегая к загрузке файла на сервер, и не прибегая к использованию MemoryStream.

Есть такой метод для загрузки файла на сервер:

    protected async Task<IActionResult> UploadFile(
        IFormFile file,
        string apiKey,
        string folderForCryptionFile)
    {
        try
        {
            if (file == null || file.Length <= 0)
            {
                return BadRequest(new { message = "No file selected for upload" });
            }

            var userid = await _readUser.ReadUserIdByApiKey(apiKey);
            var uploadsFolder = Path.Combine(folderForCryptionFile, $"User({userid})");
            if (!Directory.Exists(uploadsFolder))
            {
                Directory.CreateDirectory(uploadsFolder);
            }

            double totalSizeFolder = GetSize.GetFolderSizeInMb(uploadsFolder);
            double totalSizeFile = GetSize.GetFileSizeInMb(file);
            if (totalSizeFolder + totalSizeFile > 75.0)
            {
                return BadRequest("The size of an individual folder should not exceed 75 MB");
            }

            var uniqueFileName = Guid.NewGuid().ToString() + "_" + file.FileName;
            var filePath = Path.Combine(uploadsFolder, uniqueFileName);

            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await file.CopyToAsync(stream);
            }

            return Ok();
        }
        catch (InvalidOperationException)
        {
            return BadRequest("User with this API key not found");
        }
        catch (Exception ex)
        {
            return Problem(ex.Message);
        }
    }

И такой метод контроллера:

    [HttpPost("Upload")]
    public async Task<IActionResult> UploadFiles(List<IFormFile> files, [FromHeader(Name = "ApiKey")] string apiKey)
    {
        var totalFiles = files.Count;
        var successFiles = 0;

        foreach (var file in files)
        {
            if (allowedFiles.allowedContentTypes.Contains(file.ContentType))
            {
                await UploadFile(file, apiKey, _paths.FolderForEncryptionFiles);
                successFiles++;
            }
            else
            {
                continue;
            }
        }

        if (successFiles == totalFiles)
        {
            return Ok($"All {totalFiles} files were successfully uploaded and saved.");
        }
        else if (successFiles > 0 && successFiles != totalFiles)
        {
            return Ok($"{successFiles} out of {totalFiles} files were successfully uploaded and saved.");
        }
        else
        {
            return BadRequest("No files were successfully uploaded.");
        }
    }

Как видно он принимает файл от клиента в виде IFormFile, и в контроллере перед загрузкой файла надо будет читать файл и отправлять данные файла на сторонее API, и так как прибегать к загрузке файла на сервер нельзя, и использовать File.OpenRead следовательно нельзя, остается вариант с MemoryStream, но он может использовать большое кол-во памяти, и если файлов и пользователей будет много, то using, Dispose уже не помогут.

Есть ли ли еще какой-нибудь вариант чтения файла ?

Ответы

▲ 1Принят

IFormFile это, по факту, обертка над Stream, который был получен из запроса. Она позволяет создать Stream при помощи метода OpenReadStream, который уже можно использовать, чтобы копировать данные без сохранения в файл.

Вы такой подход уже использовали тут:

using (var stream = new FileStream(filePath, FileMode.Create))
{
    await file.CopyToAsync(stream);
}

По-этому при отправке этого файла на стороннее API вы может попробовать использовать тот же подход. Т.е. если у вас есть доступ к потоку, например, NetworkStream, то вы можете использовать CopyToAsync.

Если вы используете HttpClient, вы можете попробовать упаковать этот поток в Content:

var stream = file.OpenReadStream();

// ...

using var content = new MultipartFormDataContent();
content.Add(new StreamContent(stream), "<request_field>", "<file_name>");

Если же у вас какое-то особенное API, то, скорее всего, это нужно будет реализовать вручную, читать в буфер, каким-то образом обрабатывать и отправлять дальше.