Serus
Serus

Reputation: 154

Convert an object to JSON, then download that JSON as a text file [Asp.net core]

Info and code:

I am designing a UI where the user can design an object to meet their needs. Afterwards, I want them to be able to click a button to download a file containing the JSON representation of this object. A jquery click listener will use ajax to hit the endpoint on the controller when the button is clicked. Currently, the endpoint looks like this:

   // GET: api/Missions/DownloadMission?id
    [HttpGet]
    [Route("api/Missions/DownloadMission{id}")]
    public IHttpActionResult DownloadMission(int id)
    {
        Mission toDownload = db.Missions.Find(id);
        string json = JsonConvert.SerializeObject(toDownload);
    }

As you can see, the mission object's Id is provided to controller, and the mission is grabbed from it. My problem is that I do not know how to convert the object into JSON in a way that I can then write said JSON into a file, and prompt the user to download it.


Things I have tried:

  using (MemoryStream stream = new MemoryStream())
    {
        using (StreamWriter writer = new StreamWriter(stream))
        {
            while(missionJson.nex)
        }
        return File(stream, "text/plain");
    }

//I tried playing around with this type of set up, but could still not get the intended results 
   byte[] bytes = System.IO.File.ReadAllBytes(data);
    var output = new FileContentResult(bytes, "application/octet-stream");
    output.FileDownloadName = "download.txt";

    return output;
    Mission toDownload = db.Missions.Find(id);
    string fileName = @"~\Mission.txt";
    try
    {
        if (File.Exists(fileName))
        {
            File.Delete(fileName);
        }  
        using (FileStream fs = File.Create(fileName))
        {
            JsonSerializer serializer = new JsonSerializer();
            serializer.Converters.Add(new JavaScriptDateTimeConverter());
            serializer.NullValueHandling = NullValueHandling.Ignore;

            using (StreamWriter sw = new StreamWriter(fileName))
            using (JsonWriter writer = new JsonTextWriter(sw))
            {
                serializer.Serialize(writer, toDownload);
            }
            return File(fs, "Mission.txt");
        }   
    }
    catch (Exception Ex)
    {
        Console.WriteLine(Ex.ToString());
    }
// In this case, "File()" isnt recognized, but this is probably the closest i've been
  1. I have looked through questions such as this one

Problems:

  1. Like I said earlier, I don't know how to go from object to Json to a file
  2. basically all tutorials I can find online are very outdated, or require a filepath, presuming you are providing a pre-existing file, which I am not.

Upvotes: 3

Views: 3801

Answers (3)

AnthonyLambert
AnthonyLambert

Reputation: 8830

Server Side built c# .Net 8.

_ = builder.MapGet
    (
        EndPointName + @"downloadFile/{filePath}",
        async (string filePath, StaticVoiceFileConfigFactory staticConfig) =>
        {
            filePath = Path.Combine(staticConfig.VoiceFileDir(), filePath);


            FileInfo fi = new FileInfo(filePath);
            if (!fi.Exists)
            {
                //Error;
            }

            var mimeType = "application/octet-stream";
            var bytes = await System.IO.File.ReadAllBytesAsync(filePath);
            return new FileContentResult(bytes, mimeType);
        }
        
    )
    .Produces((int)StatusCodes.Status404NotFound)
    .Produces((int)StatusCodes.Status200OK, typeof(FileContentResult))
    .WithName($"GET {EndPointName} Download File")
    .WithTags(EndPointTag);

Client side

  ...

  var agent = vfRest.GET_Calls_Download_FileAsync(jsonFile, stoppingToken);
  int statusCode = agent.Result.StatusCode;

  var filename = agent.Result;

  if (statusCode == 200)
  {
      string json = new StreamReader(agent.Result.Stream).ReadToEnd();
     
     //This doesnt work as it doesnt have correct constructors for deserialising in Microsoft.AspNetCore.Mvc;

     //FileContentResult? model = Newtonsoft.Json.JsonConvert.DeserializeObject<FileContentResult>(json);

     // lets unpack it ourselves.

      string startStr = @"{""fileContents"":""";
      String endStr = @",""contentType";

      if (json.StartsWith(startStr))
      {
          int startLen = json.LastIndexOf(endStr);

          string endStr2 = json[startLen..];
          string fileStr = json.Substring(startStr.Length, startLen - startStr.Length - 1);

          byte[] data = Convert.FromBase64String(fileStr);
       
          var f =  File.WriteAllBytesAsync("c:/temp/{jsonFile}", data, stoppingToken);
          f.Wait();
      }
  }

This code writes the file to disk you could choose to deserialise to a class if the file contents represents a class which can be deserialised.

The GET_Calls_Download_FileAsync function is written for me by the NSAG compiler you would substitute for your own code and is beyond the scoppe of the question.

Upvotes: 0

andreas
andreas

Reputation: 331

This could work for you:

[HttpGet]
[Route("api/Missions/DownloadMission{id}")]
public IHttpActionResult DownloadMission(int id)
{
    var toDownload = db.Missions.Find(id);
       
    // if you set this, the browser asks the user to save 'export.json'
    HttpContext.Response.Headers.Add("Content-Disposition", "attachment; filename=export.json");

    // return content as json with your default json serializer settings
    return new JsonResult(toDownload);
}

Like pointed out before, replace the IHttpActionResult with IActionResult, that's the right return type for ASP.NET Core.

I didn't try how it behaves with larger objects, maybe there is some optimization needed for that.


Alternatively you can also set the header in a more "fancy" way, that will properly escape your file name and gives you some additional properites to set.

HttpContext.Response.Headers.Add("Content-Disposition", new System.Net.Mime.ContentDisposition
{
    FileName = "export.json",
    Inline   = false
}.ToString());

Upvotes: 0

Yihui Sun
Yihui Sun

Reputation: 813

  1. Like I said earlier, I don't know how to go from object to Json to a file

    1. You can use the System.Text.Json namespace to serialize and deserialize JavaScript Object Notation (JSON).
    2. string jsonString = JsonSerializer.Serialize(weatherForecast);
  2. basically all tutorials I can find online are very outdated, or require a filepath, presuming you are providing a pre-existing file, which I am not.

    1. You can return FileContentResult.

    2. You can check the example below.

    3. Code

       [HttpGet("DownloadMission/{id}")]
       public FileContentResult DownloadMission(int id)
       {
           Mission toDownload = new Mission { Id = 1, name = "test" };
           string jsonString = JsonSerializer.Serialize(toDownload);
           var fileName = "test.txt";
           var mimeType = "text/plain";
           var fileBytes = Encoding.ASCII.GetBytes(jsonString);
           return new FileContentResult(fileBytes, mimeType)
           {
               FileDownloadName = fileName
           };
       }
      
    4. Result

      enter image description here

Upvotes: 2

Related Questions