I have very simple example to export some data to Excel file, in some reason I see the bytes in the response but no file downloaded why?
public async Task<IActionResult> exportRecordsToExcel()
{
var file = await ServiceRequestBL.ExportFO_SrviceRequestToExcel();
return file;
}
public async Task<FileStreamResult> ExportFO_SrviceRequestToExcel()
{
DataTable dt = new DataTable("Grid");
dt.Columns.AddRange(new DataColumn[13] {
new DataColumn("1"), new DataColumn("2"), new DataColumn("g"), new DataColumn("j"), new DataColumn("k"), new DataColumn("l"), new DataColumn("x"), new DataColumn("m"), new DataColumn("9"), new DataColumn("8"), new DataColumn("7"), new DataColumn("6"), new DataColumn("5")});
byte[] data = null;
using (MemoryStream stream = new MemoryStream())
{
IFormatter bf = new BinaryFormatter();
dt.RemotingFormat = SerializationFormat.Binary;
bf.Serialize(stream, dt);
data = stream.ToArray();
}
var memoryStream = new MemoryStream();
memoryStream.Seek(0, SeekOrigin.Begin);
string filename = "Report.xlsx";
return new FileStreamResult(memoryStream, "application/ms-excel") { FileDownloadName = filename };
}
You do not need a using block when using FileStreamResult, as this wrapper will take care of disposing the stream when it is no longer needed (it already uses using internally).
Simply serialize your data into a stream with Serialize(stream, ...) as you have, and pass that stream over to FileStreamResult. Let it take of care of the rest.
You are passing an empty MemoryStream rather than the one you stored the data in
public Task<FileStreamResult> ExportFO_SrviceRequestToExcel()
{
var dt = new DataTable("Grid")
{
RemotingFormat = SerializationFormat.Binary,
Columns =
{
{"1"}, {"2"}, {"g"}, {"j"}, {"k"}, {"l"}, {"x"},
{"m"}, {"9"}, {"8"}, {"7"}, {"6"}, {"5"}
},
};
var stream = new MemoryStream();
var bf = new BinaryFormatter();
bf.Serialize(stream, dt);
memoryStream.Seek(0, SeekOrigin.Begin);
string filename = "Report.xlsx";
return Task.FromResult(new FileStreamResult(memoryStream, "application/ms-excel") { FileDownloadName = filename });
}
The stream does not need a using because FileStreamResult will dispose it, and in any case it's a MemoryStream which is backed only by an array.
Also, in this case there is no need for async as you are not awaiting anything.
I note that BinaryFormatter is deprecated and has security issues. Consider using another serializer if possible.
Related
I am implementing the BLOB Storing capability provided through the Volo.Abp.BlobStoring.IBlobContainer interface.
I believe I have everything coded and configured correctly but a recent deprecation by Microsoft has me wondering if there is a better implementation than what I am attempting.
Here is my code:
public async Task StorePhotoAsync(Photo photo)
{
var photoBytes = ObjectToByteArray(photo);
await _blobContainer.SaveAsync(photo.Id.ToString(), photoBytes);
}
// TODO - Reimplement (deprecated serialization) - JLavallet 2022-03-09 10:41
private byte[] ObjectToByteArray(object obj)
{
if (obj == null)
{
return null;
}
var bf = new BinaryFormatter();
using var ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
// TODO - Reimplement (deprecated serialization) - JLavallet 2022-03-09 10:41
private object ByteArrayToObject(byte[] arrBytes)
{
using var memStream = new MemoryStream();
var binForm = new BinaryFormatter();
memStream.Write(arrBytes, 0, arrBytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
var obj = binForm.Deserialize(memStream);
return obj;
}
As you can see from my to do comments and the screenshot below, there is a deprecation of the Serialize and Deserialize methods of the BinaryFormatter:
Could anyone suggest an alternative approach? The IBlobContainer only wants to save a byte array.
So after Mr. T set me straight, I read the documentation for JSON UTF8 serialization and deserialization, and here's what I came up with:
public async Task StorePhotoCacheItemAsync(PhotoCacheItem photoCacheItem)
{
var bytes = PhotoCacheItemToByteArray(photoCacheItem);
await _blobContainer.SaveAsync(photoCacheItem.Id.ToString(), bytes);
}
private byte[] PhotoCacheItemToByteArray(PhotoCacheItem photoCacheItem)
{
if (photoCacheItem == null)
{
return null;
}
// the old way
//var bf = new BinaryFormatter();
//using var ms = new MemoryStream();
//bf.Serialize(ms, obj);
// a new way
//using var ms = new MemoryStream();
//using var writer = new Utf8JsonWriter(ms);
//JsonSerializer.Serialize(writer, photoCacheItem);
//return ms.ToArray();
// a better new way
var bytes = JsonSerializer.SerializeToUtf8Bytes(photoCacheItem);
return bytes;
}
private async Task<PhotoCacheItem> GetPhotoCacheItemFromBlobStorage(Guid photoId)
{
var bytes = await _blobContainer.GetAllBytesOrNullAsync(photoId.ToString());
if (bytes == null)
{
return null;
}
var photoCacheItem = ByteArrayToPhotoCacheItem(bytes);
return photoCacheItem;
}
private PhotoCacheItem ByteArrayToPhotoCacheItem(byte[] bytes)
{
// the old way
//using var ms = new MemoryStream();
//var bf = new BinaryFormatter();
//ms.Write(bytes, 0, bytes.Length);
//ms.Seek(0, SeekOrigin.Begin);
//var obj = bf.Deserialize(ms);
// a new way
//var utf8Reader = new Utf8JsonReader(bytes);
//var photoCacheItem = JsonSerializer.Deserialize<PhotoCacheItem>(ref utf8Reader)!;
// a better new way
var readOnlySpan = new ReadOnlySpan<byte>(bytes);
var photoCacheItem = JsonSerializer.Deserialize<PhotoCacheItem>(readOnlySpan)!;
return photoCacheItem;
}
My initial requirement is to let the user download a file from object list for that I found this solution https://stackoverflow.com/a/49207997/11178128,
But the problem is when it comes to this line
bin = stream.ToArray();
there are no streams written to it. So the bin comes as an empty array.
What could be the problem?
Also, I'm making my web API available through a windows service. And for some reason System.Web.HttpContext.Current.Response gives me null. any idea why it can be?
Thanks in advance.
This is the code i have so far
List<Device> devices;
using (StreamReader r = new StreamReader(String.Format(#"{0}\deviceList.json", savefilePath)))
{
string json = r.ReadToEnd();
devices = JsonConvert.DeserializeObject<List<Device>>(json);
}
byte[] bin;
//String.Format(#"{0}\devices.csv", savefilePath)
using (MemoryStream stream = new MemoryStream())
using (TextWriter textWriter = new StreamWriter(stream))
using (CsvWriter csv = new CsvWriter(textWriter))
{
csv.Configuration.ShouldQuote = (field, context) => false;
csv.WriteRecords(devices);
bin = stream.ToArray();
}
This is related to another question, CsvHelper not writing anything to memory stream.
You just need to change your using statements so that the StreamWriter gets flushed before calling stream.ToArray();
List<Device> devices;
using (StreamReader r = new StreamReader(String.Format(#"{0}\deviceList.json", savefilePath)))
{
string json = r.ReadToEnd();
devices = JsonConvert.DeserializeObject<List<Device>>(json);
}
byte[] bin;
//String.Format(#"{0}\devices.csv", savefilePath)
using (MemoryStream stream = new MemoryStream())
{
using (TextWriter textWriter = new StreamWriter(stream))
using (CsvWriter csv = new CsvWriter(textWriter))
{
csv.Configuration.ShouldQuote = (field, context) => false;
csv.WriteRecords(devices);
}
bin = stream.ToArray();
}
Actually, after a bit of struggling, Found that i was missing this line.
textWriter.Flush();
As mentioned in the below reply I had to flush the textWriter object in order to write to the file. Here is the working code.
byte[] data;
using (MemoryStream stream = new MemoryStream())
using (TextWriter textWriter = new StreamWriter(stream))
using (CsvWriter csv = new CsvWriter(textWriter))
{
csv.Configuration.RegisterClassMap<DeviceMap>();
csv.Configuration.ShouldQuote = (field, context) => false;
csv.WriteRecords(values);
textWriter.Flush();
data = stream.ToArray();
}
return data;
using (var ms = new MemoryStream())
{
using (var writer = new StreamWriter(ms))
using (var csv = new CsvWriter(writer))
{
csv.WriteRecords(dbresponse);
} // the closing tag here is important!!It flush the streamwriter
ms.ToArray(); // or ms.GetBuffer()
}
Now the ms.ToArray() will contain the data from csvHelper
For a field variant - for example a list, which won't work using the writerecords method - you will need to use writefield. I am just submitting this here as this trifling issue caused me none too little pain.
Here is an async example:
var result = await GetListOfString();
using (var ms = new MemoryStream())
{
using (var writer = new StreamWriter(ms))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
foreach (var value in result)
{
csv.WriteField(value);
await csv.NextRecordAsync();
}
await writer.FlushAsync();
return ms.ToArray();
}
}
How do I do the equivalent in JSON.net?
public SerializedResults SerializeResults(Type queryType, IEnumerable entities)
{
var results = SerializeDynamicType(queryType);
var objList = AnonymousFns.DeconstructMany(entities, false, queryType).ToList();
var ms = new MemoryStream();
var type = objList.GetType();
var serializer = new DataContractSerializer(type);
using (ms)
{
using (GZipStream compress = new GZipStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression))
{
serializer.WriteObject(compress, objList);
}
}
results.ByteArray = ms.ToArray();
return results;
}
I am confused with this line in particular:
var serializer = new DataContractSerializer(type);
How do you do that in JSON.NET??
THANKS :-)
With JSON.NET, you don't need the type when serializing. I'm assuming that it works out the type you are passing in on its own.
So, you can get rid of this completely:
var type = objList.GetType();
var serializer = new DataContractSerializer(type);
And change this:
serializer.WriteObject(compress, objList);
To:
var json = JsonConvert.SerializeObject(objList);
Here are the JSON.Net docs for JsonConvert.
I believe you can use the BsonWriter to write to a stream. I'm not sure it will give you the exact same binary format you had before, but in concept it is the same.
public SerializedResults SerializeResults(Type queryType, IEnumerable entities)
{
var results = SerializeDynamicType(queryType);
var objList = AnonymousFns.DeconstructMany(entities, false, queryType).ToList();
var ms = new MemoryStream();
using (ms)
{
using (GZipStream compress = new GZipStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression))
{
using( BsonWriter writer = new BsonWriter(compress))
{
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(writer, objList);
}
}
}
results.ByteArray = ms.ToArray();
return results;
}
I am new to stocks in C#, I wish to send a Object in C#. Have been using BinaryWriter to send data (works fine for string), but it doesn't seem to have a method like
writer.Writer(new SerliaizedObject());
How do we achieve this using BinaryReader/BinaryWriter
UPDATE:
I used the following functions to convert by object to byte and send it across to the client
public static byte[] SerializeToBytes<T>(T item)
{
var formatter = new BinaryFormatter();
using (var stream = new MemoryStream())
{
formatter.Serialize(stream, item);
stream.Seek(0, SeekOrigin.Begin);
return stream.ToArray();
}
}
public static object DeserializeFromBytes(byte[] bytes)
{
var formatter = new BinaryFormatter();
using (var stream = new MemoryStream(bytes))
{
return formatter.Deserialize(stream);
}
}
To send the data is used:
formatter = new BinaryFormatter();
MessageBox.Show(SerializeToBytes<mydata>(new mydata()).Length+"");
writer.Write(SerializeToBytes<mydata>(new mydata()));
ChatBox.AppendText("Client Says :" + UserMessage.Text + "\r\n");
And to read the data I used:
while (true)
{
byte[] bytes = reader.ReadBytes(120);
mydata temp = DeserializeFromBytes(bytes) as mydata;
ChatBox.AppendText("Server Says " + temp + "\r\n");
}
But the reader doesn't seem to work, Any Ideas?
Use BinaryFormatter to write serializable objects to streams in binary format:
FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, mySerializable);
You should use the first 4 bytes as length header, and in the receive loop you add a variable bytesReadSoFar. Then you know when everything is received.
I use DotNetZip.
I get error
It is not possible to use PKZIP encryption on a non-seekable input
stream
what to do
var outputStream = new MemoryStream();
using (var zip = new ZipFile())
{
zip.Password = "123456!";
var outputStreamFile = new MemoryStream();
var userId = m_userRepository.GetuserByLogin(this.User.Identity.Name).UserId;
using (var streamWriter = new StreamWriter(outputStreamFile))
{
streamWriter.WriteLine(m_kamikaze2Repository.GetGameById(gameId, userId).Result);
}
zip.AddEntry("result_" + gameId, outputStreamFile);
zip.Save(outputStream);//error
}
The problem is that outpuStreamFile is closed when you add it to the ZipFile. StreamWriter.Dispose will dispose the stream so just move the using statement to ensure that outputStreamFile is available when you call zip.Save.
Also, before adding outputStreamFile to the ZipFile you need to rewind it.
using (var outputStreamFile = new MemoryStream()) {
var userId = m_userRepository.GetuserByLogin(this.User.Identity.Name).UserId;
var streamWriter = new StreamWriter(outputStreamFile);
streamWriter.WriteLine(m_kamikaze2Repository.GetGameById(gameId, userId).Result);
outputStreamFile.Seek(0, SeekOrigin.Begin);
zip.AddEntry("result_" + gameId, outputStreamFile);
zip.Save(outputStream);
}
Try like this:
using (var outputStream = new MemoryStream())
using (var zip = new ZipFile())
{
zip.Password = "123456!";
zip.AddEntry("result_" + gameId, "Some content");
zip.Save(outputStream);
byte[] zipFile = outputStream.ToArray();
// TODO: do something with the zip
}