Reputation: 154
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.
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
Upvotes: 3
Views: 3801
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
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
Reputation: 813
Like I said earlier, I don't know how to go from object to Json to a file
string jsonString = JsonSerializer.Serialize(weatherForecast);
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.
You can return FileContentResult.
You can check the example below.
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
};
}
Result
Upvotes: 2