Search code examples
c#asp.net-coreio

Read Text file without copying to hard disk


I'm using Asp.Net Core 3.0 and I find myself in a situation where the client will pass text file(s) to my API, the API will then parse the text files into a data model using a function that I have created called ParseDataToModel(), and then store that data model into a database using Entity Framework. Since my code is parsing the files into a data model, I really don't need to copy it to the hard disk if it isn't necessary. I don't have a ton of knowledge when it comes to Streams, and I've googled quite a bit, but I was wondering if there is a way to retrieve the string data of the uploaded files without actually copying them to the hard drive? It seems like a needless extra step.... Below is my code for the file Upload and insertion into the database:

 [HttpPost("upload"), DisableRequestSizeLimit]
    public IActionResult Upload()
    {
        var filePaths = new List<string>();
        foreach(var formFile in Request.Form.Files)
        {
            if(formFile.Length > 0)
            {
                var filePath = Path.GetTempFileName();
                filePaths.Add(filePath);

                using(var stream = new FileStream(filePath, FileMode.Create))
                {
                    formFile.CopyTo(stream);
                }
            }
        }

        BaiFiles lastFile = null;
        foreach(string s in filePaths)
        {
            string contents = System.IO.File.ReadAllText(s);
            BaiFiles fileToCreate = ParseFileToModel(contents);


            if (fileToCreate == null)
                return BadRequest(ModelState);

            var file = _fileRepository.GetFiles().Where(t => t.FileId == fileToCreate.FileId).FirstOrDefault();

            if (file != null)
            {
                ModelState.AddModelError("", $"File with id {fileToCreate.FileId} already exists");
                return StatusCode(422, ModelState);
            }

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

            if (!_fileRepository.CreateFile(fileToCreate))
            {
                ModelState.AddModelError("", $"Something went wrong saving file with id {fileToCreate.FileId}");
                return StatusCode(500, ModelState);
            }

            lastFile = fileToCreate;


        }
        return CreatedAtRoute("GetFile", new { fileId = lastFile.FileId }, lastFile);
    }

It would be nice to just hold all of the data in memory instead of copying them to the hard drive, just to turn around and open it to read the text.... I apologize if this isn't possible, or if this question has been asked before. I'm sure it has, and I just wasn't googling the correct keywords. Otherwise, I could be wrong and it is already doing exactly what I want - but System.IO.File.ReadAllText() makes me feel it's being copied to a temp directory somewhere.

After using John's answer below, here is the revised code for anyone interested:

[HttpPost("upload"), DisableRequestSizeLimit]
    public IActionResult Upload()
    {
        var filePaths = new List<string>();
        BaiFiles lastFile = null;
        foreach (var formFile in Request.Form.Files)
        {
            if (formFile.Length > 0)
            {
                using (var stream = formFile.OpenReadStream())
                {
                    using (var sr = new StreamReader(stream))
                    {
                        string contents = sr.ReadToEnd();

                        BaiFiles fileToCreate = ParseFileToModel(contents);


                        if (fileToCreate == null)
                            return BadRequest(ModelState);

                        var file = _fileRepository.GetFiles().Where(t => t.FileId == fileToCreate.FileId).FirstOrDefault();

                        if (file != null)
                        {
                            ModelState.AddModelError("", $"File with id {fileToCreate.FileId} already exists");
                            return StatusCode(422, ModelState);
                        }

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

                        if (!_fileRepository.CreateFile(fileToCreate))
                        {
                            ModelState.AddModelError("", $"Something went wrong saving file with id {fileToCreate.FileId}");
                            return StatusCode(500, ModelState);
                        }

                        lastFile = fileToCreate;
                    }
                }

            }  
        }
        if(lastFile == null)
           return NoContent();
        else
            return CreatedAtRoute("GetFile", new { fileId = lastFile.FileId }, lastFile);
    }

Solution

  • System.IO.File.ReadAllText(filePath) is a convenience method. It essentially does this:

    string text = null;
    
    using (var stream = FileStream.OpenRead(filePath))
    using (var reader = new StreamReader(stream))
    {
        text = reader.ReadToEnd();
    }
    

    FormFile implements an OpenReadStream method, so you can simply use this in place of stream in the above:

    string text = null;
    
    using (var stream = formFile.OpenReadStream())
    using (var reader = new StreamReader(stream))
    {
        text = reader.ReadToEnd();
    }