I have an asp.net/C#/Blazor environment, where a button generates an XML with a specific class. With XML Writer, I can make the file, and even can save/download it, but it goes to the server-side (It must to be downloaded on client-side. please, do not argue about it).
I know Blazor implemented some instant download (https://learn.microsoft.com/en-us/aspnet/core/blazor/file-downloads?view=aspnetcore-6.0) and it works perfect with blank/new files, but the problem is, I don't know how to "pass" or convert my previously generated XML with XML Writer method, because Blazor method(s) only allow Stream, char or byte Arrays downloads.
When I tried to convert it, the error
Some of my code is:
protected async Task CreateXmlFile(int correlativo,string idDocumento, string siglaDocumento, List<DocumentoXML> documentos = null, List<SignersXML> signersXMLs = null,
List<ContentXMLComplemento> complementos = null,
List<SignersXMLComplemento> signersComplemento = null)
{
_xmlWriterSettings = new XmlWriterSettings
{
Indent = true,
Encoding = new UTF8Encoding(false)
};
string fullPath= "";
XmlWriter writer;
XmlSerializer serializer;
var documentoIngresoRaiz = new DocumentoIngresoRaiz
{
Content_XML = new List<ContentXML>
{
new ContentXML
{
sve_XML = new List<sveXML>
{
new sveXML
{
Documento_XML = documentos
}
}
}
},
Signers_XML = signersXMLs
};
fullPath = $"{mainPath}Ingreso-{correlativo}.xml";
var fileName = $"Ingreso-{correlativo}.xml";
writer = XmlWriter.Create(fullPath, _xmlWriterSettings);
serializer = new XmlSerializer(typeof(DocumentoIngresoRaiz));
serializer.Serialize(writer, documentoIngresoRaiz);
writer.Close();
//I've been trying with these 3 Blazor method lines, to send my xml as stream
var fileStream = new MemoryStream(writer);
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
Error CS1503: Argument 1: cannot convert from 'System.Xml.XmlWriter' to 'byte[]'
I've been looking all around StackOverflow and the Internet with no success.
I found some similar posts (I want to download XML file in C# which I created using XmlWriter.Create() on user's system) (How to get a Stream from an XMLWriter?), but they couldn't solve my problem. Any help or tip is welcome. Thank you in advance!
Since there was no way to convert the already generated XML file to byte/stream/char array, I found out that the solution was:
saving this XML file on server-side
and then immediately download it to local machine, via JavaScript code (pasted below), passing the fileURL (location of file on the server) and fileName (name of the file)
await JS.InvokeVoidAsync("triggerFileDownload", fileName, fileURL);
function triggerFileDownload(fileName, url) {
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = fileName ?? '';
anchorElement.click();
anchorElement.remove();
}
Related
I'm currently looking extracting all of the JSON Schemas from a large OpenAPI spec. I've been using the following NuGet packages:
Microsoft.OpenApi v1.3.1
Microsoft.OpenApi.Readers v1.3.1
I was hoping to use these to parse a large Open API spec and extract all of the JSON Schemas, which I am able to parse into 'Microsoft.OpenApi.Models.OpenApiSchema' objects. But I can't seem to create a JSON Schema from these objects and write it to file.
As it stands at the moment I have the following:
using (FileStream fs = File.Open(file.FullName, FileMode.Open))
{
var openApiDocument = new OpenApiStreamReader().Read(fs, out var diagnostic);
foreach (var schema in openApiDocument.Components.Schemas)
{
var schemaName = schema.Key;
var schemaContent = schema.Value;
var outputDir = Path.Combine(outputDirectory.FullName, fileNameWithoutExtension);
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
var outputPath = Path.Combine(outputDir, schemaName + "-Schema.json");
var outputString = schemaContent.Serialize(OpenApiSpecVersion.OpenApi3_0, OpenApiFormat.Json);
using (TextWriter sw = new StreamWriter(outputPath, true))
{
sw.Write(outputString);
sw.Close();
}
}
}
The schemaContent appears to have all of the relevant properties for the schema, but I don't seem to be able to identify the next step in getting it from that object to a JSON Schema. I'm sure I'm missing something simple so any insight would be appreciated.
UPDATED
I had a bit of a think and took a slightly different approach using NewtonSoft Json instead.
var OpenApitext = File.ReadAllText(file.FullName, Encoding.UTF8);
var settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
MetadataPropertyHandling = MetadataPropertyHandling.Ignore, //ign
Formatting = Newtonsoft.Json.Formatting.Indented
};
dynamic openApiJson = JsonConvert.DeserializeObject<ExpandoObject>(OpenApitext, settings);
if (openApiJson?.components?.schemas != null)
{
foreach (var schema in openApiJson.components.schemas)
{
var schemaString = JsonConvert.SerializeObject(schema, settings);
var outputDir = Path.Combine(outputDirectory.FullName, fileNameWithoutExtension);
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
var outputPath = Path.Combine(outputDir, schema.Name + "-Schema.json");
using (TextWriter sw = new StreamWriter(outputPath, true))
{
sw.Write(schemaString);
sw.Close();
}
}
}
Now this will allow me to create the JSON Schema and write it to file, but it doesn't want to resolve references. Looking at the API spec all references appear to be local to the API Spec. What do I need to do in order to resolve all the references in the Open API Spec before I cycle through the schemas and write them to file? I've done a bit of research and a few people seem to build out this capability themselves, but they always use a class object as a way of supporting it which I can't do here.
I reached out through the microsoft/OpenAPI.NET GitHub repo in the end. By a bit of a coincidence/happenstance I got a response from the same person both there and here. So, thank you Darrel you've helped me solve the above scenario which I was getting rather confused over. I knew in the end it was that I hadn't quite implemented it correctly.
For reference the below use case was to take in a sizeable OpenAPI Spec (Json) and extract the JSON Schemas referenced whilst ensuring that the JSON Pointers ($ref, $id) etc were resolved when this was written out to file.
The reason this was the approach I wanted to take was that due to the size of the OpenAPI specs I had to work with it was incredibly difficult using pre-built tooling like Postman for example which can extract Schemas.
Final code snippet for my implementation, little rough on a couple of the lines, I'll neaten that up over the weekend.
Console.WriteLine($"Processing file: {file.FullName}");
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file.FullName);
var fileExtension = Path.GetExtension(file.FullName);
var reader = new OpenApiStreamReader();
var result = await reader.ReadAsync(new FileStream(file.FullName, FileMode.Open));
foreach (var schemaEntry in result.OpenApiDocument.Components.Schemas)
{
var schemaFileName = schemaEntry.Key + ".json";
Console.WriteLine("Creating " + schemaFileName);
var outputDir = Path.Combine(outputDirectory.FullName, fileNameWithoutExtension);
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
var outputPath = Path.Combine(outputDir, schemaFileName + "-Schema.json");
using FileStream? fileStream = new FileStream(outputPath, FileMode.CreateNew);
var writerSettings = new OpenApiWriterSettings() { InlineLocalReferences = true, InlineExternalReferences = true };
using var writer = new StreamWriter(fileStream);
schemaEntry.Value.SerializeAsV2WithoutReference(new OpenApiJsonWriter(writer, writerSettings));
}
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
I have looked through questions such as this one
Problems:
Like I said earlier, I don't know how to go from object to Json to a file
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.
Like I said earlier, I don't know how to go from object to Json to a file
You can use the System.Text.Json namespace to serialize and deserialize JavaScript Object Notation (JSON).
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
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());
I am creating excel file by convert .cshtml MVC Razor page into bytes and then converting it into Memory stream to create excel, but when opening that excel file I am getting a warning message, I want to ignore it or you can say remove it. How can I do that ( don't want to use External library like NPOI or EPPlus)
Here is my C# Code
var html = FakeController.RenderViewToString2(ControllerContext, "~/Views/Report/DistirbuterPriceListExcel.cshtml", distributerListModel, true);
var bytes = Encoding.ASCII.GetBytes(html);
MemoryStream ms = new MemoryStream(bytes);
var root = Server.MapPath("~/Uploads/ReportPDFFiles/");
var pathtosave = "../../Uploads/ReportPDFFiles/";
var CompURL = "";
var FileName = "";
var pdfname = String.Format("{0}.xls", Guid.NewGuid().ToString());
var pdfCompleteName = "Distributer-List.xls";
var path = Path.Combine(root, pdfname);
var path2 = Path.Combine(root, pdfCompleteName);
path = Path.GetFullPath(path);
path2 = Path.GetFullPath(path2);
var fileStream2 = new FileStream(path2, FileMode.Create, FileAccess.Write);
fileStream2.Write(bytes, 0, bytes.Length);
fileStream2.Close();
CompURL = pathtosave + pdfCompleteName;
FileName = pdfCompleteName;
return Json(new { CompURL, FileName }, JsonRequestBehavior.AllowGet);
Where distributerListModel= Model passed to view, I want to return url in JSON response.
I can create excel file, but it is opening and showing results, but showing warning error "the file you are trying to open is in different format", here is the image
I want to ignore or remove the above error, how can I do that?
As the HTML layout of table is quite complicated I don't want to use EPPlus and directly want to convert html/css code into excel.
Background: relatively new to C# and NetSuite.
Given a little netsuite method (via SuiteTalk) such as:
private void getInvoice()
{
RecordRef invoiceRef = new RecordRef
{
internalId = "111111",
type = RecordType.invoice,
typeSpecified = true
};
ReadResponse readResponse = _service.get(invoiceRef);
}//eof
How would I get the entirety of the readResponse as a file? It is an XML file on the front-end...can I download / read that to a file at the end of this script? I don't know if its being treated as a stream here or not either, which would make it a little easier to just turn it into a file.
In your example, a request would be made to NetSuite and the response will be loaded into your "readResponse" variable as a ReadResponse class. You would then need to convert the record returned into an Invoice:
Invoice invoiceRecord = (Invoice)readResponse.record;
if you want to write the response to a file, you could do something like this:
ReadResponse readResponse = _service.get(invoiceRef);
FileStream fs = System.IO.File.Create("response.xml");
XmlSerializer writer = new XmlSerializer(typeof(ReadResponse));
writer.Serialize(fs, readResponse);
fs.Close();
I have been following these links all listed below, i found the best way to write this SMALL create Excel and Download function. ( Using EPPlus for Excel )
Download file of any type in Asp.Net MVC using FileResult? + How to convert an Stream into a byte[] in C#?
Using a FileStreamResult with a MemoryStream in ASP.NET MVC 3
Writing A Custom File Download Action Result For ASP.NET MVC
It runs through the code perfectly without error every time I run this but does not "Kick out" the file to be downloaded ( in a save as dialogue or w/e ).
public ActionResult ShowReport()
{
using (var stream = new MemoryStream())
{
ExcelPackage pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("Sample1");
ws.Cells["A1"].Value = "Sample 1";
ws.Cells["A1"].Style.Font.Bold = true;
var shape = ws.Drawings.AddShape("Shape1", eShapeStyle.Rect);
shape.SetPosition(50, 200);
shape.SetSize(200, 100);
shape.Text = "Sample 1 text text text";
var fileDownloadName = "sample.xlsx";
var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";//System.Net.Mime.MediaTypeNames.Application.Octet
var fileStream = new MemoryStream();
pck.SaveAs(fileStream);
fileStream.Position = 0;
var fsr = new FileStreamResult(fileStream, contentType);
fsr.FileDownloadName = fileDownloadName;
byte[] fileBytes = ReadToEnd(fileStream);
string fileName = "example";
return File(fileBytes, contentType, fileName);
}
}
What am I doing wrong / missing? - Must i write that Dialogue myself?
PN: I have also attempted this way
byte[] fileBytes = ReadToEnd(fileStream);
string fileName = "example";
return File(fileBytes, contentType, fileName);
ofcourse i had to figure out how to convert Stream to Byte but it also did not show anything.
Image of Chrome's Network Development Tool
Sorry about the small image ( if you can't see it scroll in with ctl+MouseWheel ) if your in a supporting browswer.
(In response to the comment thread above.)
From the image posted it looks like the actual file request (the last one in the list) is coming from JavaScript code instead of from a normal document-level request. Given this, it's highly likely that the server-side code is working correctly and returning the correct response.
However, since it's an AJAX request, the browser doesn't actually know what to do with the response. There are some potential solutions here. Ideally, you'll want to make this a normal request and remove AJAX from the picture if possible. If that's not an option, you can still initiate a document-level request from JavaScript. Something as simple as this:
window.location = '#Url.Action("Method", "Controller")';
This would be initiated from JavaScript code as it currently is, but would be for the whole browser instead of an AJAX request. That should do the trick.
Using the memory stream you have you can simple pass that to the Response object once you have saved the Excel Package
Code:
Response.AddHeader("content-disposition", "attachment;filename=FILENAME.xlsx")
Response.Charset = String.Empty
Response.ContentType = "application/ms-excel"
Response.BinaryWrite(stream.ToArray())
Response.End()