3

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.
2
  • Have you tried to simply return Ok(toDownload); this will return the json object without serialization Commented Jun 3, 2021 at 21:38
  • @Jawad I need to have the user download a file containing said JSON rather than just returning the json Commented Jun 3, 2021 at 21:45

3 Answers 3

2
  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

Sign up to request clarification or add additional context in comments.

Comments

0

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());

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.