C#: looking for file stream seek between 2 length - c#

I would like to copy the partial content of a file using FileStream seek.
the actual file stream length of the file = 98764.
I want file content between length 200 and 5000
If I am doing fileStream.Seek(200, SeekOrigin.Begin), then this give me file content from 200 to 98764, but I want from 200 and 5000.
public static async Task<HttpResponseMessage> Get()
{
using (var fileStream = new FileStream(#"C:\test\tes1.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var actualFileStremSize = fileStream.Length;
//fileStream.Seek(200, SeekOrigin.Begin);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(fileStream),
};
using (FileStream fs = new FileStream(#"C:\test\tes1_partial_copy.txt", FileMode.CreateNew, FileAccess.Write))
{
await result.Content.CopyToAsync(fs);
}
return result;
}
}
}
Main Method,
class Program
{
static void Main(string[] args)
{
Get().Wait();
Console.ReadLine();
}
Looking for a solution to get file content between 2 file stream length. Thanks,

Related

Changes are not save when using MemoryStream instead of FileStream

I have a DLL with embedded Excel file. The goal is to retrieve this file and create some entry (Empty_File.txt in this example). When I'm using FileStream - the entry gets created, but when I'm using MemoryStream - entry isn't created.
var filePath = "C:\\Temp\\Test2.xlsx";
var asm = typeof(Program).Assembly;
var asmName = asm.GetName().Name;
using var resourceStream = asm.GetManifestResourceStream($"{asmName}.Resources.Template.xlsx");
if (File.Exists(filePath)) File.Delete(filePath);
await UseFileStream(resourceStream, filePath);
// or
await UseMemoryStream(resourceStream, filePath);
static async Task UseMemoryStream(Stream resourceStream, string filePath)
{
using (var ms = new MemoryStream())
{
await resourceStream.CopyToAsync(ms);
using (var zip = new ZipArchive(ms, ZipArchiveMode.Update))
{
zip.CreateEntry("Empty_File.txt");
using (var fs = CreateFileStream(filePath))
{
ms.Seek(0L, SeekOrigin.Begin);
await ms.CopyToAsync(fs);
}
}
}
}
static async Task UseFileStream(Stream resourceStream, string filePath)
{
using var fs = CreateFileStream(filePath);
await resourceStream.CopyToAsync(fs);
using var zip = new ZipArchive(fs, ZipArchiveMode.Update);
zip.CreateEntry("Empty_File.txt");
}
static FileStream CreateFileStream(string filePath) =>
new FileStream(filePath, new FileStreamOptions
{
Access = FileAccess.ReadWrite,
Mode = FileMode.Create,
Share = FileShare.None
});
Per the docs for ZipArchive.Dispose:
This method finishes writing the archive and releases all resources used by the ZipArchive object. Unless you construct the object by using the ZipArchive(Stream, ZipArchiveMode, Boolean) constructor overload and set its leaveOpen parameter to true, all underlying streams are closed and no longer available for subsequent write operations.
You are currently writing to the file stream before this happens, so the changes to the zip file haven't been written yet.
You'll also note from this that the underlying MemoryStream will be disposed unless you specify leaveOpen: true in the constructor, which would prevent you copying to the file afterwards.
So putting both of these together:
static async Task UseMemoryStream(Stream resourceStream, string filePath)
{
using (var ms = new MemoryStream())
{
await resourceStream.CopyToAsync(ms);
using (var zip = new ZipArchive(ms, ZipArchiveMode.Update, leaveOpen: true))
{
zip.CreateEntry("Empty_File.txt");
}
using (var fs = CreateFileStream(filePath))
{
ms.Seek(0L, SeekOrigin.Begin);
await ms.CopyToAsync(fs);
}
}
}

Unable to create zip file using Ionic.Zip

I'm not sure where and what am I doing wrong, but the zip that I'm creating using DotNetZip library, is creating a zip file whose contents are blank. Or the size of file in zip is showing as 0Kb and unable to open it.
Code:
public static async Task DotNetZipFileAsync(MemoryStream stream, string bucket, List<List<string>> pdfFileSet, IAmazonS3 s3Client)
{
using Ionic.Zip.ZipFile zip = new ZipFile();
foreach (var pdfFile in pdfFileSet)
{
foreach (var file in pdfFile)
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucket,
Key = file
};
using GetObjectResponse response = await s3Client.GetObjectAsync(request);
using Stream responseStream = response.ResponseStream;
ZipEntry zipEntry = zip.AddEntry(file.Split('/')[^1], responseStream);
await responseStream.CopyToAsync(stream);
}
}
zip.Save(stream);
stream.Seek(0,SeekOrigin.Begin);
await stream.CopyToAsync(new FileStream(#"C:\LocalRepo\Temp.zip", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite));
}
}
}
Your code has at least two problems:
The read stream is completely consumed by the await responseStream.CopyToAsync(stream). You could rewind the responseStream to cope with this, but saving the data into the memory stream is completely useless.
The response stream is disposed before zip.Save is called.
What you could do: keep the streams open until Save is called and dispose them afterwards. As Alexey Rumyantsev discovered (see comments), also the GetObjectResponse objects need to be kept until the ZIP file is saved.
using Ionic.Zip.ZipFile zip = new ZipFile();
var disposables = List<IDisposable>();
try
{
foreach (var pdfFile in pdfFileSet)
{
foreach (var file in pdfFile)
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucket,
Key = file
};
var response = await s3Client.GetObjectAsync(request);
disposables.Add(response);
var responseStream = response.ResponseStream;
disposables.Add(responseStream);
ZipEntry zipEntry = zip.AddEntry(file.Split('/')[^1], responseStream);
}
}
using var fileStream = new FileStream(#"C:\LocalRepo\Temp.zip", FileMode.Create, FileAccess.Write);
zip.Save(fileStream);
}
finally
{
foreach (var disposable in disposables)
{
disposable.Dispose();
}
}
The documentation has some hints ony how this could be made smarter.
public static async Task DotNetZipFileAsync(string bucket, List<List<string>> pdfFileSet, IAmazonS3 s3Client)
{
int read;
using Ionic.Zip.ZipFile zip = new ZipFile();
byte[] buffer = new byte[16 * 1024];
foreach (var pdfFile in pdfFileSet)
{
foreach (var file in pdfFile)
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucket,
Key = file
};
using GetObjectResponse response = await s3Client.GetObjectAsync(request);
using Stream responseStream = response.ResponseStream;
using (MemoryStream ms = new MemoryStream())
{
while ((read = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
zip.AddEntry(file.Split('/')[^1], ms.ToArray());
}
}
}
using var fileStream = new FileStream(#"C:\LocalRepo\Temp.zip", FileMode.Create, FileAccess.Write);
zip.Save(fileStream);
}

Saving pdf stream in a file as a pdf

I have a variable which holds a pdf stream , this variable is of type System.Threading.Tasks.Task<Stream>. I want to save this pdf stream in a pdf file but I am not sure how to do so . Below is a piece of code I tried to work on . Any ideas as to what I can try to save this stream in a file
System.Threading.Tasks.Task<Stream> pdf = //Some logic here which gets a pdf stream
I want to store the pdf content in the variable in a file as a pdf
For that I worote the method
public static void SaveStreamAsFile(string filePath, System.Threading.Tasks.Task<Stream> inputStream, string fileName)
{
string path = Path.Combine(filePath, fileName);
using (FileStream outputFileStream = new FileStream(path, FileMode.Create))
{
// logic
}
}
Read the input stream and write it to the output stream..
public static async Task SaveStreamAsFile(string filePath, System.Threading.Tasks.Task<Stream> inputStream, string fileName)
{
var stream = await inputStream;
var path = Path.Combine(filePath, fileName);
var bytesInStream = new byte[stream.Length];
await stream.ReadAsync(bytesInStream, 0, (int) bytesInStream.Length);
using (var outputFileStream = new FileStream(path, FileMode.Create))
{
await outputFileStream.WriteAsync(bytesInStream, 0, bytesInStream.Length);
}
}

Getting extra space in last line after file seek and zipping it

Using below code I am using File seek and convert to result byte to compressed stream and generating the zip file,
public static async Task Get(string filename)
{
byte[] result;
byte[] compressedBytes;
using (FileStream SourceStream = File.Open(filename, FileMode.Open))
{
SourceStream.Seek(20, SeekOrigin.Begin);
result = new byte[SourceStream.Length];
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
}
string fileName = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var outStream = File.Create(fileName))
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry("test.txt", CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(result))
{
fileToCompressStream.CopyTo(entryStream);
}
}
}
}
Now when I unzip the resultant file has extra space. What's the reason for it and how to resolve it?
You're seeking 20 bytes into the stream, but the length of your array is the complete length of the stream. Therefore the final 20 bytes in your array are being ignored.
The simple fix for this is just to allocate less space, and then only ask to read the reduced number of bytes:
result = new byte[SourceStream.Length - 20];
await SourceStream.ReadAsync(result, 0, result.Length);
Note that you're also assuming that a single call to ReadAsync will read all the data. That may be the case in many situations, but it's generally not a good idea to assume that about streams.
It would be simpler just to copy straight from the file stream to the compressed stream though, instead of reading the whole file into memory first:
public static async Task Get(string filename)
{
string outputFile = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var outStream = File.Create(outputFile))
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry("test.txt", CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = File.Open(filename, FileMode.Open))
{
// Skip the first 20 bytes
fileToCompressStream.Position = 20;
fileToCompressStream.CopyTo(entryStream);
}
}
}
}

How to reset the stream of StreamReader to beginning?

I have referred this Return StreamReader to Beginning, but couldn't figure out this problem.
This is code to read stream of a particular file in zip file. Here there are two stream of files inside two different zip files. Now I need to compare the streams.
I am unable to set the stream of BaseFileReader stream to beginning of stream.
using (FileStream BaseZipToOpen = new FileStream(BaseArchive,FileMode.Open) , CurrentZipToOpen = new FileStream(CurrentArchive,FileMode.Open))
{
using (ZipArchive BaseZip = new ZipArchive(BaseZipToOpen, ZipArchiveMode.Read), CurrentZip = new ZipArchive(CurrentZipToOpen, ZipArchiveMode.Read))
{
ZipArchiveEntry BaseFile = BaseZip.GetEntry(requiredFile);
ZipArchiveEntry CurrentFile = CurrentZip.GetEntry(requiredFile);
using (StreamReader BaseFileReader = new StreamReader(BaseFile.Open()), CurrentFileReader = new StreamReader(CurrentFile.Open()))
{
string baseFileLine, currentFileLine;
while (!CurrentFileReader.EndOfStream)
{
currentFileLine = CurrentFileReader.ReadLine();
while (!BaseFileReader.EndOfStream)
{
baseFileLine = BaseFileReader.ReadLine();
if (!currentFileLine.Equals(baseFileLine))
{
difference = true;
}
else
{
difference = false;
break;
}
}
// how to reset BaseFileReader Stream to beginning?
BaseZipToOpen.Seek(0, SeekOrigin.Begin); //This is not working
}
}
}
}
You can use
FileStream stream = new FileStream();
stream.Position = 0;

Categories