I have a dlm file and I want to create a .tar.gz file from the content in dlm file. When I am trying to create the file, it is created but when I manually unzip that it is failed. My code is below for creating .tar.gz file, targetFileName is like C:\Folder\xxx.tar.gz:
using (StreamWriter write = new StreamWriter(targetFileName, false, Encoding.Default))
{
write.Write(text.ToString());
write.Close();
}
In the above code text is content from dlm file. Is there anything that I am missing? please help.
try use SharpZipLib from Nuget
using System;
using System.IO;
using System.Text;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
add method:
private static void CreateTarGZ(string tgzFilename, string innerFilename, string text)
{
var uncompressed = Encoding.UTF8.GetBytes(text);
using (Stream outStream = File.Create(tgzFilename))
{
using (GZipOutputStream gzoStream = new GZipOutputStream(outStream))
{
gzoStream.SetLevel(9);
using (TarOutputStream taroStream = new TarOutputStream(gzoStream, Encoding.UTF8))
{
taroStream.IsStreamOwner = false;
TarEntry entry = TarEntry.CreateTarEntry(innerFilename);
entry.Size = uncompressed.Length;
taroStream.PutNextEntry(entry);
taroStream.Write(uncompressed, 0, uncompressed.Length);
taroStream.CloseEntry();
taroStream.Close();
}
}
}
}
then call:
CreateTarGZ("test.tar.gz", "FileName.txt", "my text");
CreateTarGZ("c:\\temp\\test.tar.gz", "foo-folder\\FileName.txt", "my text");
This is a quick example to create a .tar.gz and .gz file that will include the file that you might be creating using the stream.
Note that I'm using SharpZipLib which you can find in Nuget Package Manager for you project. Then make sure to add reference in your code:
Making tar.gz
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
using System.IO;
using System.Text;
static void Main(string[] args)
{
string text = ".Net is Awesome";
string filename = "D:\\text.txt";
string tarfilename = "D:\\text.tar.gz";
using (StreamWriter write = new StreamWriter(filename, false, Encoding.Default))
{
//Writing a text file
write.Write(text.ToString());
write.Close();
//Creating a tar.gz Stream
Stream TarFileStream = File.Create(tarfilename);
Stream GZStream = new GZipOutputStream(TarFileStream);
TarArchive tarArchive = TarArchive.CreateOutputTarArchive(GZStream);
tarArchive.RootPath = "D:/"; //Setting the Root Path for the archive
//Creating a file entry for the tar archive
TarEntry tarEntry = TarEntry.CreateEntryFromFile(filename);
//Writing the entry in the archive.
tarArchive.WriteEntry(tarEntry, false); //set false to only add the concerned file in the archive.
tarArchive.Close();
}
}
Making only .gz
You can create a method to make it more reusable like:
private static void MakeGz(string targetFile)
{
string TargetGz = targetFile + ".gz";
using (Stream GzStream = new GZipOutputStream(File.Create(TargetGz)))
{
using (FileStream fs = File.OpenRead(targetFile))
{
byte[] FileBuffer = new byte[fs.Length];
fs.Read(FileBuffer, 0, (int)fs.Length);
GzStream.Write(FileBuffer, 0, FileBuffer.Length);
fs.Close();
GzStream.Close();
}
}
}
Then you can call this method whenever you are creating a file to make an archive for the same at the same time like:
MakeGz(filename);
Related
I used OpenXML tools to create a byte array of the word and excel file and zip them using ZipArchive and return the filebyte.
httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK);
httpResponseMessage.Content = new ByteArrayContent(zipFileBytes);
httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
httpResponseMessage.Content.Headers.ContentLength = zipFileBytes.Length;
httpResponseMessage.Content.Headers.Add("xfilename", zipFileName);
httpResponseMessage.StatusCode = HttpStatusCode.OK;
httpResponseMessage.Content.Headers.Add("Access-Control-Expose-Headers", "xfilename");
return httpResponseMessage;
The file can be download and open after unzip the zip file.
However, it cannot be review by window explorer or other unzip software.
When trying to open the document in the window explorer, error message
"Windows cannot complete the extraction. The destination file could
not be created"
Any ideas about how to solve this issue? Can the documents be review inside the zip which created by OpenXML?
UPDATE:
I'm using "Open XML SDK 2.5 Productivity Tool" to generate the code. And the code below is the one generate the document. (For detail, please use the tool to generate the code, since it is toooooooo many line of them)
using DocumentFormat.OpenXml.Packaging;
using Ap = DocumentFormat.OpenXml.ExtendedProperties;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;
using M = DocumentFormat.OpenXml.Math;
using Ovml = DocumentFormat.OpenXml.Vml.Office;
using V = DocumentFormat.OpenXml.Vml;
using W15 = DocumentFormat.OpenXml.Office2013.Word;
using A = DocumentFormat.OpenXml.Drawing;
using Thm15 = DocumentFormat.OpenXml.Office2013.Theme;
namespace GeneratedCode
{
public class GeneratedClass
{
// Creates a WordprocessingDocument.
public void CreatePackage(DataModel dataModel, List<DataModel> dataList, out string filename, out Byte[] fileBytes)
{
filename = string.Empty;
fileBytes = null;
using (MemoryStream ms = new MemoryStream())
{
try
{
using (WordprocessingDocument package = WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document))
{
CreateParts(package, dataModel, dataList);
}
string extension = ".docx";
filename = "TestDoc" + extension;
fileBytes = ms.ToArray();
ms.Close();
return;
}
catch (System.Exception)
{
throw;
}
}
}
}
Then, I generate the zip file using the code below and passing the list of array byte from the CreatePackage function.
public byte[] zipByteDocument(List<Tuple<Byte[], string>> fileBytes)
{
// the output bytes of the zip
byte[] zipFileBytes = null;
// create a working memory stream
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
{
// create a zip
using (System.IO.Compression.ZipArchive zip = new System.IO.Compression.ZipArchive(memoryStream, System.IO.Compression.ZipArchiveMode.Create, true))
{
// interate through the source files
foreach (Tuple<Byte[], string> file in fileBytes)
{
// add the item name to the zip
System.IO.Compression.ZipArchiveEntry zipItem = zip.CreateEntry(file.Item2);
// add the item bytes to the zip entry by opening the original file and copying the bytes
using (System.IO.MemoryStream originalFileMemoryStream = new System.IO.MemoryStream(file.Item1))
{
using (System.IO.Stream entryStream = zipItem.Open())
{
originalFileMemoryStream.CopyTo(entryStream);
}
}
}
}
zipFileBytes = memoryStream.ToArray();
}
return zipFileBytes;
}
And finally, I pass the zipFileBytes to the httpResponseMessage and it can be download. But fail to be preview without unzipping the zip file.
I've created a few unit tests (see below), which demonstrate that there should not be an issue with the code you have shared (noting, however, that I have not simply replicated your code). There is a chance that the generated code or other code that you did not share is the culprit.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using CodeSnippets.IO;
using Xunit;
namespace CodeSnippets.Tests.IO.Compression
{
public class ZipArchiveTests
{
private static byte[] CreateZipArchiveBytes(IEnumerable<(byte[], string)> files)
{
using MemoryStream stream = CreateZipArchiveStream(files);
return stream.ToArray();
}
private static MemoryStream CreateZipArchiveStream(IEnumerable<(byte[], string)> files)
{
var stream = new MemoryStream();
using (CreateZipArchive(stream, files))
return stream;
}
private static ZipArchive CreateZipArchive(Stream stream, IEnumerable<(byte[], string)> files)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
if (files == null) throw new ArgumentNullException(nameof(files));
var archive = new ZipArchive(stream, ZipArchiveMode.Create, true);
foreach ((byte[] fileContent, string fileName) in files)
{
ZipArchiveEntry archiveEntry = archive.CreateEntry(fileName);
using Stream entryStream = archiveEntry.Open();
entryStream.Write(fileContent, 0, fileContent.Length);
}
return archive;
}
private static ZipArchive ReadZipArchive(byte[] zipArchiveBytes)
{
return new ZipArchive(new MemoryStream(zipArchiveBytes), ZipArchiveMode.Read, false);
}
private static byte[] ReadEntryBytes(ZipArchive zipArchive, string entryName)
{
ZipArchiveEntry entry = zipArchive.GetEntry(entryName) ?? throw new Exception();
var entryBytes = new byte[entry.Length];
using Stream entryStream = entry.Open();
entryStream.Read(entryBytes, 0, (int) entry.Length);
return entryBytes;
}
private static HttpResponseMessage CreateResponseMessage(byte[] content, string fileName, string mediaType)
{
var message = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(content)
};
message.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
message.Content.Headers.ContentType = new MediaTypeHeaderValue(mediaType);
message.Content.Headers.ContentLength = content.Length;
return message;
}
[Fact]
public async Task CreateResponseMessage_ZipArchiveBytes_Success()
{
// Arrange.
const string path = "Resources\\ZipContents.docx";
string fileName = Path.GetFileName(path);
byte[] fileContent = File.ReadAllBytes(path);
byte[] zipArchiveBytes = CreateZipArchiveBytes(new[]
{
(fileContent, fileName)
});
// Act.
using HttpResponseMessage message = CreateResponseMessage(zipArchiveBytes, "ZipArchive.zip", "application/zip");
HttpContent messageContent = message.Content;
byte[] messageBytes = await messageContent.ReadAsByteArrayAsync();
// Assert.
// Original zipArchiveBytes and recevied messageBytes are equal.
Assert.Equal(zipArchiveBytes, messageBytes);
// Original file content and received ZIP archive content are equal.
using ZipArchive zipArchive = ReadZipArchive(messageBytes);
byte[] entryContent = ReadEntryBytes(zipArchive, fileName);
Assert.Equal(fileContent.Length, entryContent.Length);
Assert.Equal(fileContent, entryContent);
}
[Fact]
public void CreateZipArchiveBytes_WordDocument_ZipFileSuccessfullyCreated()
{
// Arrange.
const string path = "Resources\\ZipContents.docx";
string fileName = Path.GetFileName(path);
byte[] fileContent = File.ReadAllBytes(path);
// Act.
byte[] zipArchiveBytes = CreateZipArchiveBytes(new[]
{
(fileContent, fileName)
});
File.WriteAllBytes("ZipArchive_Bytes.zip", zipArchiveBytes);
// Assert.
using ZipArchive zipArchive = ReadZipArchive(zipArchiveBytes);
byte[] entryContent = ReadEntryBytes(zipArchive, fileName);
Assert.Equal(fileContent.Length, entryContent.Length);
Assert.Equal(fileContent, entryContent);
}
}
}
Update 2019-12-06
I amended the unit tests to also demonstrate that this works with multiple documents. Here's the first one:
[Fact]
public void CreateZipArchiveBytes_Directory_ZipFileSuccessfullyCreated()
{
// Arrange, creating a ZIP archive with more than one entry.
List<(byte[] fileContent, string fileName)> files = Directory
.EnumerateFiles("Resources")
.Select(path => (File.ReadAllBytes(path), Path.GetFileName(path)))
.ToList();
Assert.True(files.Count > 1);
// Act.
byte[] zipArchiveBytes = CreateZipArchiveBytes(files);
File.WriteAllBytes("ZipArchive_Directory.zip", zipArchiveBytes);
// Assert.
using ZipArchive zipArchive = ReadZipArchive(zipArchiveBytes);
Assert.Equal(files.Count, zipArchive.Entries.Count);
foreach (ZipArchiveEntry entry in zipArchive.Entries)
{
byte[] fileContent = files
.Where(file => file.fileName == entry.Name)
.Select(file => file.fileContent)
.Single();
using Stream entryStream = entry.Open();
byte[] entryContent = entryStream.ToArray();
Assert.Equal(fileContent, entryContent);
}
}
The next unit test demonstrates the same in conjunction with the HttpResponseMessage.
[Fact]
public async Task CreateResponseMessage_ZipArchiveDirectory_Success()
{
// Arrange, creating a ZIP archive with more than one entry.
List<(byte[] fileContent, string fileName)> files = Directory
.EnumerateFiles("Resources")
.Select(path => (File.ReadAllBytes(path), Path.GetFileName(path)))
.ToList();
Assert.True(files.Count > 1);
byte[] zipArchiveBytes = CreateZipArchiveBytes(files);
// Act.
using HttpResponseMessage message = CreateResponseMessage(zipArchiveBytes, "ZipArchive.zip", "application/zip");
HttpContent messageContent = message.Content;
byte[] messageBytes = await messageContent.ReadAsByteArrayAsync();
// Assert.
// Original zipArchiveBytes and recevied messageBytes are equal.
Assert.Equal(zipArchiveBytes, messageBytes);
// Original directory content and received ZIP archive content are equal.
using ZipArchive zipArchive = ReadZipArchive(messageBytes);
Assert.Equal(files.Count, zipArchive.Entries.Count);
foreach (ZipArchiveEntry entry in zipArchive.Entries)
{
byte[] fileContent = files
.Where(file => file.fileName == entry.Name)
.Select(file => file.fileContent)
.Single();
await using Stream entryStream = entry.Open();
byte[] entryContent = await entryStream.ToArrayAsync();
Assert.Equal(fileContent, entryContent);
}
}
The core methods have not changed.
The full source code can be found in my CodeSnippets GitHub repository.
I have built an asp net web api. I need to return a zipfile, as a result of some inner logic. I'm using this code and it works, but the resulting zip file, when unzipped manually, gave me this error "There are data after the end of the payload"
using (ZipFile zip = new ZipFile())
{
...
zip.Save(di.FullName + "\\" + "Update.zip");
}
string path = Path.Combine(Properties.Settings.Default.PathDisposizioniHTML, "Update.zip");
var response = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new System.IO.FileStream(path, System.IO.FileMode.Open);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
This is how i receive the data in a .net console application:
using (Stream output = File.OpenWrite(#"C:\prova\MyFile.zip"))
using (Stream input = httpResponse.GetResponseStream())
{
input.CopyTo(output);
}
If you already have the zip file on your system, you shouldn't need to do anything special before sending it as a response.
This should work:
string filePath = #"C:\myfolder\myfile.zip";
return File(filePath, "application/zip");
If you're making the file on the fly, i.e. getting other files and programatically putting them into a zip file for the user, the following should work:
public IActionResult GetZipFile(){
//location of the file you want to compress
string filePath = #"C:\myfolder\myfile.ext";
//name of the zip file you will be creating
string zipFileName = "zipFile.zip";
byte[] result;
using (MemoryStream zipArchiveMemoryStream = new MemoryStream())
{
using (ZipArchive zipArchive = new ZipArchive(zipArchiveMemoryStream, ZipArchiveMode.Create, true))
{
ZipArchiveEntry zipEntry = zipArchive.CreateEntry(zipFileName);
using (Stream entryStream = zipEntry.Open())
{
using (MemoryStream tmpMemory = new MemoryStream(System.IO.File.ReadAllBytes(filePath)))
{
tmpMemory.CopyTo(entryStream);
};
}
}
zipArchiveMemoryStream.Seek(0, SeekOrigin.Begin);
result = zipArchiveMemoryStream.ToArray();
}
return File(result, "application/zip", zipFileName);
}
This is taken from a recent ASP.NET project of my own.
I have some files inside in one .tar.gz archive. These files are on a linux server.How can I read from a specific file inside this archive if I know it's name?
For reading direct from the txt file, I used the following code:
Uri urlFile = new Uri("ftp://" + ServerName + "/%2f" + FilePath + "/" + fileName);
WebClient req = new WebClient() { Credentials=new NetworkCredential("user","psw")};
string result = req.DownloadString(urlFile);
It's possible to read this file without copying the archive on the local machine, something like the code above?
I found a solution. Maybe this can help you guys.
// archivePath="ftp://myLinuxServer.com/%2f/move/files/archive/20170225.tar.gz";
public static string ExtractFileFromArchive(string archivePath, string fileName)
{
string stringFromFile="File not found";
WebClient wc = new WebClient() { Credentials = cred, Proxy= webProxy }; //Create webClient with all necessary settings
using (Stream source = new GZipInputStream(wc.OpenRead(archivePath))) //wc.OpenRead() create one stream with archive tar.gz from our server
{
using (TarInputStream tarStr =new TarInputStream(source)) //TarInputStream is a stream from ICSharpCode.SharpZipLib.Tar library(need install SharpZipLib in nutgets)
{
TarEntry te;
while ((te = tarStr.GetNextEntry())!=null) // Go through all files from archive
{
if (te.Name == fileName)
{
using (Stream fs = new MemoryStream()) //Create a empty stream that we will be fill with file contents.
{
tarStr.CopyEntryContents(fs);
fs.Position = 0; //Move stream position to 0, in order to read from beginning
stringFromFile = new StreamReader(fs).ReadToEnd(); //Convert stream to string
}
break;
}
}
}
}
return stringFromFile;
}
I am required to read the contents of an .xml file using the Stream (Here the xml file is existing with in the zip package). Here in the below code, I need to get the file path at runtime (here I have hardcoded the path for reference). Please let me know how to read the file path at run time.
I have tried to use string s =entry.FullName.ToString(); but get the error "Could not find the Path". I have also tried to hard code the path as shown below. however get the same FileNotFound error.
string metaDataContents;
using (var zipStream = new FileStream(#"C:\OB10LinuxShare\TEST1\Temp" + "\\"+zipFileName+".zip", FileMode.Open))
using (var archive = new ZipArchive(zipStream, ZipArchiveMode.Read))
{
foreach (var entry in archive.Entries)
{
if (entry.Name.EndsWith(".xml"))
{
FileInfo metadataFileInfo = new FileInfo(entry.Name);
string metadataFileName = metadataFileInfo.Name.Replace(metadataFileInfo.Extension, String.Empty);
if (String.Compare(zipFileName, metadataFileName, true) == 0)
{
using (var stream = entry.Open())
using (var reader = new StreamReader(stream))
{
metaDataContents = reader.ReadToEnd();
clientProcessLogWriter.WriteToLog(LogWriter.LogLevel.DEBUG, "metaDataContents : " + metaDataContents);
}
}
}
}
}
I have also tried to get the contents of the .xml file using the Stream object as shown below. But here I get the error "Stream was not readable".
Stream metaDataStream = null;
string metaDataContent = string.Empty;
using (Stream stream = entry.Open())
{
metaDataStream = stream;
}
using (var reader = new StreamReader(metaDataStream))
{
metaDataContent = reader.ReadToEnd();
}
Kindly suggest, how to read the contents of the xml with in a zip file using Stream and StreamReader by specifying the file path at run time
Your section code snippet is failing because when you reach the end of the first using statement:
using (Stream stream = entry.Open())
{
metaDataStream = stream;
}
... the stream will be disposed. That's the point of a using statment. You should be fine with this sort of code, but load the XML file while the stream is open:
XDocument doc;
using (Stream stream = entry.Open())
{
doc = XDocument.Load(stream);
}
That's to load it as XML... if you really just want the text, you could use:
string text;
using (Stream stream = entry.Open())
{
using (StreamReader reader = new StreamReader(stream))
{
text = reader.ReadToEnd();
}
}
Again, note how this is reading before it hits the end of either using statement.
Here is a sample of how to read a zip file using .net 4.5
private void readZipFile(String filePath)
{
String fileContents = "";
try
{
if (System.IO.File.Exists(filePath))
{
System.IO.Compression.ZipArchive apcZipFile = System.IO.Compression.ZipFile.Open(filePath, System.IO.Compression.ZipArchiveMode.Read);
foreach (System.IO.Compression.ZipArchiveEntry entry in apcZipFile.Entries)
{
if (entry.Name.ToUpper().EndsWith(".XML"))
{
System.IO.Compression.ZipArchiveEntry zipEntry = apcZipFile.GetEntry(entry.Name);
using (System.IO.StreamReader sr = new System.IO.StreamReader(zipEntry.Open()))
{
//read the contents into a string
fileContents = sr.ReadToEnd();
}
}
}
}
}
catch (Exception)
{
throw;
}
}
I'm attempting to open a Word document, change some text and then save the changes to a new document. I can get the first bit done using the code below but I can't figure out how to save the changes to a NEW document (specifying the path and file name).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using DocumentFormat.OpenXml.Packaging;
using System.IO;
namespace WordTest
{
class Program
{
static void Main(string[] args)
{
string template = #"c:\data\hello.docx";
string documentText;
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(template, true))
{
using (StreamReader reader = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
documentText = reader.ReadToEnd();
}
documentText = documentText.Replace("##Name##", "Paul");
documentText = documentText.Replace("##Make##", "Samsung");
using (StreamWriter writer = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
writer.Write(documentText);
}
}
}
}
}
I'm a complete beginner at this, so forgive the basic question!
If you use a MemoryStream you can save the changes to a new file like this:
byte[] byteArray = File.ReadAllBytes("c:\\data\\hello.docx");
using (MemoryStream stream = new MemoryStream())
{
stream.Write(byteArray, 0, (int)byteArray.Length);
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(stream, true))
{
// Do work here
}
// Save the file with the new name
File.WriteAllBytes("C:\\data\\newFileName.docx", stream.ToArray());
}
In Open XML SDK 2.5:
File.Copy(originalFilePath, modifiedFilePath);
using (var wordprocessingDocument = WordprocessingDocument.Open(modifiedFilePath, isEditable: true))
{
// Do changes here...
}
wordprocessingDocument.AutoSave is true by default so Close and Dispose will save changes.
wordprocessingDocument.Close is not needed explicitly because the using block will call it.
This approach doesn't require entire file content to be loaded into memory like in accepted answer. It isn't a problem for small files, but in my case I have to process more docx files with embedded xlsx and pdf content at the same time so the memory usage would be quite high.
Simply copy the source file to the destination and make changes from there.
File.copy(source,destination);
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(destination, true))
{
\\Make changes to the document and save it.
wordDoc.MainDocumentPart.Document.Save();
wordDoc.Close();
}
Hope this works.
This approach allows you to buffer the "template" file without batching the whole thing into a byte[], perhaps allowing it to be less resource intensive.
var templatePath = #"c:\data\hello.docx";
var documentPath = #"c:\data\newFilename.docx";
using (var template = File.OpenRead(templatePath))
using (var documentStream = File.Open(documentPath, FileMode.OpenOrCreate))
{
template.CopyTo(documentStream);
using (var document = WordprocessingDocument.Open(documentStream, true))
{
//do your work here
document.MainDocumentPart.Document.Save();
}
}
For me this worked fine:
// To search and replace content in a document part.
public static void SearchAndReplace(string document)
{
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, true))
{
string docText = null;
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}
Regex regexText = new Regex("Hello world!");
docText = regexText.Replace(docText, "Hi Everyone!");
using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
sw.Write(docText);
}
}
}