Load report from file created from Stream in FastReport - c#

There is a method that builds reports. It generates stream on server and then save it to a tmp file.
public virtual void Build(ReportType reportType, ReportParameters parameters)
{
Export = reportType.Id == MaterialReportIds.WarehouseBalanceDynamic;
var tempFileName = Path.GetTempFileName();
try {
ServiceManager<IMaterialsReportsDataService>.Invoke(service =>
{
using (var stream = service.BuildReport(reportType, parameters)) {
using (var fileStream = File.Create(tempFileName)) {
int buf;
while ((buf = stream.ReadByte()) >= 0) {
fileStream.WriteByte((byte)buf);
}
fileStream.Flush();
fileStream.Close();
stream.Close();
}
}
});
Report.LoadPrepared(tempFileName);
}
finally {
File.Delete(tempFileName);
}
}
On service :
public Stream BuildReport(ReportType reportType, ReportParameters parameters) {
IReport report = new ReportFactory(Assembly.GetExecutingAssembly()).CreateReport(reportType);
try {
report.Build(reportType, parameters);
MemoryStream stream = new MemoryStream();
(report.Object as Stream)?.CopyTo(stream);
stream.Seek(0, SeekOrigin.Begin);
return stream;
} finally {
(report?.Object as IDisposable)?.Dispose();
}
}
After that we use Report.LoadPrepared(path to a tmp file) and it works, but when I try to use Report.Load(path to a tmp file) it doesn't. Do anybody know why? The error says that file format is incorrect, but why Report.LoadPrepared() works?

Just guessing from the documentation FastReport provided here.
Load method accepts ".frx" files:
report.Load("report1.frx");
From the other hand LoadPrepared accepts ".fpx" files:
Report.LoadPrepared(this.Server.MapPath("~/App_Data/Prepared.fpx"));

Related

Issue in disposing Stream objects for asp.net mvc application?

I have asp.net mvc application which has file upload functionality. While uploading the file, I am performing few validations on the uploaded content before moving it to database and file system location.
Here goes my code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddImage([Bind(Include = "image,ImageName,ImageType,CountryId,Keyword,Source,Copyright,Description")] CreateImageViewModel model)
{
if (!this.ModelState.IsValid)
{
return View("Images");
}
if (model != null && model.image.ContentType.Contains(Constants.Image) && !ValidateUploadedImageContent(model.image, model.image.FileName))
{
var dto = new ImageDTO();
model.FilePath = model.image.FileName;
dto.ImageFile = model.image;
dto.Name = model.ImageName;
dto.FilePath = model.image.FileName;
dto.FileType = Path.GetExtension(model.FilePath);
dto.ImageType = model.ImageType;
dto.CountryId = model.CountryId;
dto.Keyword = model.Keyword;
dto.Source = model.Source;
dto.Copyright = model.Copyright;
dto.Description = model.Description;
dto.CreatedBy = UserDto.emailId;
try
{
_imageService.SaveImage(dto);
}
catch (Exception ex)
{
if (ex.Message.Equals(Constants.InvalidImageType))
return GetSafeRedirect(Url.Action("AddImage", model) + "#onload-errors");
throw ex;
}
return RedirectToAction(Constants.Actions.Images.ToString());
}
else
{
return GetSafeRedirect(Url.Action("AddImage", model) + "#onload-errors");
}
}
private bool ValidateUploadedImageContent(HttpPostedFileBase uploadedFile, string imageFileName)
{
if (Path.GetExtension(imageFileName).Equals(".svg", StringComparison.OrdinalIgnoreCase))
{
if (uploadedFile.ContentLength > 0)
{
byte[] data;
//using (Stream inputStream = uploadedFile.InputStream)
//{
Stream inputStream = uploadedFile.InputStream;
var memoryStream = inputStream as MemoryStream;
if (memoryStream == null)
{
memoryStream = new MemoryStream();
inputStream.CopyTo(memoryStream);
}
data = memoryStream.ToArray();
//}
var parsedData = Encoding.UTF8.GetString(data, 0, data.Length).TrimEnd('\0');
var result = parsedData.ContainsAny(Constants.InsecureStrings, StringComparison.CurrentCultureIgnoreCase);
return result;
}
}
return false;
}
Here in the above method: ValidateUploadedImageContent(), I tried to dispose the stream object with the help of using statement but I found that if I keep the below code in the method: ValidateUploadedImageContent(), then in that case post validation process, I found on debugging that the ContentLength property is set with 0 value and finally corrupted image gets saved in the file system location.
Updated :
private bool ValidateUploadedImageContent(HttpPostedFileBase uploadedFile, string imageFileName)
{
if (Path.GetExtension(imageFileName).Equals(".svg", StringComparison.OrdinalIgnoreCase))
{
if (uploadedFile.ContentLength > 0)
{
byte[] data;
using (Stream inputStream = uploadedFile.InputStream)
{
Stream inputStream = uploadedFile.InputStream;
var memoryStream = inputStream as MemoryStream;
if (memoryStream == null)
{
memoryStream = new MemoryStream();
inputStream.CopyTo(memoryStream);
}
data = memoryStream.ToArray();
}
var parsedData = Encoding.UTF8.GetString(data, 0, data.Length).TrimEnd('\0');
var result = parsedData.ContainsAny(Constants.InsecureStrings, StringComparison.CurrentCultureIgnoreCase);
return result;
}
}
return false;
}
Can anyone help me to know how to fix this issue?
First to address your issue, which I now understand is that after the call to ValidateUploadedImageContent the image stream is invalid.
That is because the stream gained from the HttpPostedFileBase is "read-only sequential (non-seekable)" as detailed in this SO answer. This explains why the stream's ContentLength is 0 - the stream has been consumed by the validation call.
If you have flexibility with the ImageDTO class, modifying the validation method such that it returns the image bytes would be a workaround.
For example,
// on success, buffer contains the image data. Otherwise it is null.
private bool ValidateUploadedImageContent(
out byte[] buffer,
HttpPostedFileBase uploadedFile,
string imageFileName)
{
buffer = null;
if (Path.GetExtension(imageFileName).Equals(".svg", StringComparison.OrdinalIgnoreCase))
{
if (uploadedFile.ContentLength > 0)
{
var reader = new BinaryReader(inputStream);
buffer = reader.ReadBytes((int)uploadedFile.ContentLength);
var parsedData = Encoding.UTF8.GetString(buffer, 0, buffer.Length).TrimEnd('\0');
return parsedData.ContainsAny(Constants.InsecureStrings, StringComparison.CurrentCultureIgnoreCase);
}
}
return false;
}
I've used BinaryReader to simplify the code.
Then back to the calling method,
byte[] imageBuffer = null;
if (model != null && model.image.ContentType.Contains(Constants.Image)
&& !ValidateUploadedImageContent(out imageBuffer, model.image, model.image.FileName)) {
var dto = new ImageDTO();
using(var imageStream = new MemoryStream(imageBuffer)) {
// pass along imageStream to your ImageDTO and save.
}
}
Again, hopefully you have some flexibility with the ImageDTO class.

Cannot access a closed stream when passing stream to another function

I need to pass a stream to a couple of functions in another class, but its throwing an error
Cannot access a closed stream
Here's the code:
first method:
Here it opens a file with File.Open method and then creates a memorystream object and it copies FileStream to MemoryStream. then sets Position to 0 (i set position to 0, because i was that in a solution, but not helping tho). Then it creates an object of class DocxConvert and call the Converto method by passing MemoryStream to it.
using (var stream = File.Open(tempPath2, FileMode.Open))
{
using(var ms = new MemoryStream())
{
stream.CopyTo(ms);
ms.Position = 0;
using (var docx = new DocxConvert())
{
return docx.Converto(ms);
}
}
}
DocxConvert Class:
It takes stream and then calls copyStream method by passing the accepted stream.
copyStream method in DocxConvert Class: it should copy the accepted stream to another stream called _memoryStream which is a class property.
public class DocxConvert
{
private MemoryStream _memoryStream = new MemoryStream();
public bool Converto(Stream stream)
{
try
{
copyStream(stream);
//more code
}
catch (IOException ex)
{
Debug.WriteLine(ex);
}
return true;
}
private void copyStream(Stream stream)
{
stream.CopyTo(_memoryStream); //here it throws the error
}
}
p.s. I search for this error here before posting, but non of the topics helped me.
SOLVED by restarting PC, the code it ok.
I am not aware about your question .But here in the code bellow no excepion
private void button1_Click(object sender, EventArgs e)
{
string tempPath2 = Application.StartupPath + "//" + "test.txt";
using (var stream = File.Open(tempPath2, FileMode.Open))
{
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
ms.Position = 0;
var docx = new DocxConvert();
var isok = docx.Converto(ms);
}
}
}
The bellow is the calss defined where _memorystream is defined at top
MemoryStream _memoryStream = new MemoryStream();
public bool Converto(Stream stream)
{
try
{
copyStream(stream);
//more code
}
catch (IOException ex)
{
// Debug.WriteLine(ex);
}
return true;
}
private void copyStream(Stream stream)
{
try
{
stream.CopyTo(_memoryStream);
}
catch (Exception)
{
}
}

Reading from a ZipArchiveEntry cause exception and MemoryLeak if using a MemoryStream

I have the following code that generate two kinds of errors. First with the current code I get an exception 'NotSupportedException: This stream from ZipArchiveEntry does not support reading.'. How am I supposed to read the data ?
Furthermore if i use a MemoryStream (as the commented code ) then I can read the data and deserialize correctly but the memorystream i created still remains in memory even if the dispose method has been called on it , causing some memory leaks . Any idea what is wrong with this code ?
void Main()
{
List<Product> products;
using (var s = GetDb().Result)
{
products = Utf8Json.JsonSerializer.Deserialize<List<Product>>(s).ToList();
}
}
// Define other methods and classes here
public static Task<Stream> GetDb()
{
var filepath = Path.Combine("c:/users/tom/Downloads", "productdb.zip");
using (var archive = ZipFile.OpenRead(filepath))
{
var data = archive.Entries.Single(e => e.FullName == "productdb.json");
return Task.FromResult(data.Open());
//using (var reader = new StreamReader(data.Open()))
//{
// var ms = new MemoryStream();
// data.Open().CopyTo(ms);
// ms.Seek(0, SeekOrigin.Begin);
// return Task.FromResult((Stream)ms);
//}
}
}
With the commented code you open the stream into a reader, don't use the reader, then open the stream again and copy over to the memory stream without closing the second opened stream.
It is the second opened stream that remains in memory, not the MemoryStream.
Refactor
public static async Task<Stream> GetDb() {
var filepath = Path.Combine("c:/users/tom/Downloads", "productdb.zip");
using (var archive = ZipFile.OpenRead(filepath)) {
var entry = archive.Entries.Single(e => e.FullName == "productdb.json");
using (var stream = entry.Open()) {
var ms = new MemoryStream();
await stream.CopyToAsync(ms);
return ms;
}
}
}

ZipArchive returning Empty Folder C#

I am using ZipArchive to create a zipped folder for a list of documents.
I cant figure out why, when I return my archived folder it is empty.
Does anyone see what I am doing wrong here?
My code is as follows:
if (files.Count > 1)
{
var ms = new MemoryStream();
var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, false);
foreach (var file in files)
{
var entry = zipArchive.CreateEntry(file.UploadFileName, CompressionLevel.Fastest);
using (var streamWriter = new StreamWriter(entry.Open()))
{
Stream strFile = new MemoryStream(file.UploadFileBytesStream);
streamWriter.Write(strFile);
strFile.CopyTo(ms);
}
}
return File(ms, System.Net.Mime.MediaTypeNames.Application.Zip, "FinancialActivityReports.zip");
}
Assuming the following model for file
public class FileModel {
public string UploadFileName { get; set; }
public byte[] UploadFileBytesStream { get; set; }
}
The following helper was written to create the stream of the compressed files
public static class FileModelCompression {
public static Stream Compress(this IEnumerable<FileModel> files) {
if (files.Any()) {
var ms = new MemoryStream();
var archive = new ZipArchive(ms, ZipArchiveMode.Create, false);
foreach (var file in files) {
var entry = archive.add(file);
}
ms.Position = 0;
return ms;
}
return null;
}
private static ZipArchiveEntry add(this ZipArchive archive, FileModel file) {
var entry = archive.CreateEntry(file.UploadFileName, CompressionLevel.Fastest);
using (var stream = entry.Open()) {
stream.Write(file.UploadFileBytesStream, 0, file.UploadFileBytesStream.Length);
stream.Position = 0;
stream.Close();
}
return entry;
}
}
You code, assuming files is derived from IEnumerable<FileModel> will then change to this...
if (files.Count > 1)
{
var stream = files.Compress();
return File(stream, System.Net.Mime.MediaTypeNames.Application.Zip, "FinancialActivityReports.zip");
}

Read file.inputstream twice

I need to read csv file twice. but after first reading:
using (var csvReader = new StreamReader(file.InputStream))
{
fileFullText += csvReader.ReadToEnd();
file.InputStream.Seek(0, SeekOrigin.Begin);
csvReader.Close();
}
using file in enother function:
public static List<string> ParceCsv(HttpPostedFileBase file)
{
//file.InputStream.Seek(0, SeekOrigin.Begin);
using (var csvReader = new StreamReader(file.InputStream))
{
// csvReader.DiscardBufferedData();
// csvReader.BaseStream.Seek(0, SeekOrigin.Begin);
string inputLine = "";
var values = new List<string>();
while ((inputLine = csvReader.ReadLine()) != null)
{
values.Add(inputLine.Trim().Replace(",", "").Replace(" ", ""));
}
csvReader.Close();
return values;
}
}
The file.Length is 0.
Can anybody help?
The reason is that SteramReader's Dispose() method also closes the underlying stream; In your case file.InputStream. The using statement calls Dispose() implicitly. Try to replace using with disposes of both your StreamReaded-s after you finished both read operations. As I remember some stream classes have a bool option to leave underlying stream open after dispose.
.NET 4.5 fixed this issue by introducing leaveOpen parameter in SteamReader constructor. See: MSDN
public StreamReader(
Stream stream,
Encoding encoding,
bool detectEncodingFromByteOrderMarks,
int bufferSize,
bool leaveOpen
)
One more thing. You do not need to close SteramReader yourself (the line with csvReader.Close();) when you wrap it in using statement, thus Dispose() and Close() are the same in case of StreamReader.
if your using HttpPostedFileBase you need to clone it first,
use the code this git here
or just add this as a class in your namespace:
public static class HttpPostedFileBaseExtensions
{
public static Byte[] ToByteArray(this HttpPostedFileBase value)
{
if (value == null)
return null;
var array = new Byte[value.ContentLength];
value.InputStream.Position = 0;
value.InputStream.Read(array, 0, value.ContentLength);
return array;
}
}
now you can read the HttpPostedFileBase like so:
private static void doSomeStuff(HttpPostedFileBase file)
{
try
{
using (var reader = new MemoryStream(file.ToByteArray()))
{
// do some stuff... say read it to xml
using (var xmlTextReader = new XmlTextReader(reader))
{
}
}
}
catch (Exception ex)
{
throw ex;
}
}
after using this you can still write in your main code:
file.SaveAs(path);
and it will save it to the file.

Categories

Resources