Currently, I was base on "Search and replace text in a document part (Open XML SDK)" on the Microsoft site. I've realized that the code got an issue after the file has downloaded to my drive.
So I opened that file and got a message
MEMORY STREAM IS NOT EXPANDABLE at sw.Write(docText);
How to fix that?
In GenerateDocxHelper class:
private readonly MemoryStream _mem;
private Dictionary<string, string> _dicData;
public GenerateDocxHelper(string path)
{
_mem = new MemoryStream(System.IO.File.ReadAllBytes(path));
_dicData = new Dictionary<string, string>();
}
public MemoryStream ReplaceTextInWord()
{
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(_mem, true))
{
string docText = null;
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}
foreach (var data in _dicData)
{
docText = docText.Replace(data.Key, data.Value);
}
using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
sw.Write(docText);
}
}
_mem.Seek(0, SeekOrigin.Begin);
return _mem;
}
You should create the MemoryStream with capacity = 0 which means it is resizeable,
and then add the bytes you have read from the file.
var allBytes = File.ReadAllBytes(path);
//this makes _mem resizeable
_mem = new MemoryStream(0);
_mem.Write(allBytes, 0, allBytes.Length);
Check this answer
Related
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();
}
}
I'm using the following code to generate an openxml document and save it on the server and that works fine, however now i'm trying to generate it to a byte stream to serve out straight away without saving to the server but the document gets corrupted in the process.
Working Code
public static void CreateWordDoc(List<objReplace> lstReplace, String TemplateFile, String OutputPath)
{
File.Delete(OutputPath);
File.Copy(TemplateFile, OutputPath);
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(OutputPath, true))
{
string docText = null;
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
docText = sr.ReadToEnd();
foreach (var ro in lstReplace)
{
Regex regexText = new Regex(ro.Find);
docText = regexText.Replace(docText, ro.Replace);
}
using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
sw.Write(docText);
}
}
}
Code that produces currupted XML - that I want to get working
public static byte[] CreateWordDocToStream(List<objReplace> lstReplace, String TemplateFile)
{
string docText = null;
byte[] filebytes = File.ReadAllBytes(TemplateFile);
using (MemoryStream stream = new MemoryStream(filebytes))
{
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(stream, true))
{
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
docText = sr.ReadToEnd();
foreach (var ro in lstReplace)
{
Regex regexText = new Regex(ro.Find);
docText = regexText.Replace(docText, ro.Replace);
}
}
}
return Encoding.ASCII.GetBytes(docText);
}
Im a long time trying to solve one problem. I have one method that Serialize a string, follows the code:
XmlRetorno()
var algumasDef = new XmlWriterSettings {
Indent = true,
OmitXmlDeclaration = true
};
var nameSpace = new XmlSerializerNamespaces();
nameSpace.Add(string.Empty, "urn:sngpc-schema");
var meuXml = new XmlSerializer(GetType(), "urn:sngpc-schema");
using (var minhaString = new StringWriterWithEncoding(Encoding.GetEncoding("iso-8859-1"))) {
using (var escreve = XmlWriter.Create(minhaString, algumasDef)) {
meuXml.Serialize(escreve, this, nameSpace);
}
return minhaString.ToString();
}
Then, my next step is to compact that string to a zip file, my method to zip.
CompactXml()
string ziparEssaString = msg.XmlRetorno();
byte[] byteArray = new byte[ziparEssaString.Length];
int indexBA = 0;
foreach (char item in ziparEssaString.ToArray()) {
byteArray[indexBA++] = (byte)item;
}
//prepare to compress
using (MemoryStream ms = new MemoryStream()) {
using (GZipStream sw = new GZipStream(ms, CompressionMode.Compress)) {
sw.Write(byteArray, 0, byteArray.Length);
}
//transform bytes[] zip to string
byteArray = ms.ToArray();
StringBuilder sB = new StringBuilder(byteArray.Length);
foreach (byte item in byteArray) {
sB.Append((char)item);
}
return sB.ToString();
}
I need to compress a string that is formatted .xml and when I unpack I need the extension to be .xml too, my webservice return an error. Please, i need one light.
I'm developing a game using XNA and I'm storing all my data like dialogs, texts and user preferences in a XML file but I wonder how I could hide all that data to not be changed by those who may open the folder where I'm storing my files. Thanks
Sample extension class to compress/decompress XML documents
public static class Extensions
{
public static void Compress(this XDocument doc, string name)
{
byte[] buffer = Encoding.UTF8.GetBytes(doc.ToString(SaveOptions.DisableFormatting));
using (var ms = new MemoryStream(buffer.Length))
{
ms.Write(buffer,0,buffer.Length);
ms.Seek(0, SeekOrigin.Begin);
using (var fs = new FileStream(name, FileMode.Create))
{
using (var gzipStream = new GZipStream(fs, CompressionMode.Compress))
{
ms.CopyTo(gzipStream);
}
}
}
}
public static XDocument Decompress(string name)
{
using (var fs = new FileStream(name,FileMode.Open))
{
using (var ms = new MemoryStream())
{
using (var gzip = new GZipStream(fs,CompressionMode.Decompress))
{
gzip.CopyTo(ms);
}
ms.Seek(0, SeekOrigin.Begin);
string s = Encoding.UTF8.GetString(ms.ToArray());
return XDocument.Parse(s);
}
}
}
}
Sample use:
static void Main(string[] args)
{
var doc = new XDocument(
new XElement("Root", new XElement("Item1")));
doc.Compress("test1");
var doc2 = Extensions.Decompress("test1");
}
We have a system that stores some custom templating data in a Word document. Sometimes, updating this data causes Word to complain that the document is corrupted. When that happens, if I unzip the docx file and compare the contents to the previous version, the only difference appears to be the expected change in the customXML\item.xml file. If I re-zip the contents using 7zip, it seems to work OK (Word no longer complains that the document is corrupt).
The (simplified) code:
void CreateOrReplaceCustomXml(string filename, MyCustomData data)
{
using (var doc = WordProcessingDocument.Open(filename, true))
{
var part = GetCustomXmlParts(doc).SingleOrDefault();
if (part == null)
{
part = doc.MainDocumentPart.AddCustomXmlPart(CustomXmlPartType.CustomXml);
}
var serializer = new DataContractSerializer(typeof(MyCustomData));
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, data);
stream.Seek(0, SeekOrigin.Begin);
part.FeedData(stream);
}
}
}
IEnumerable<CustomXmlPart> GetCustomXmlParts(WordProcessingDocument doc)
{
return doc.MainDocumentPart.CustomXmlParts
.Where(part =>
{
using (var stream = doc.Package.GePart(c.Uri).GetStream())
using (var streamReader = new StreamReader(stream))
{
return streamReader.ReadToEnd().Contains("Some.Namespace");
}
});
}
Any suggestions?
Since re-zipping works, it seems the content is well-formed.
So it sounds like the zip process is at fault. So open the corrupted docx in 7-Zip, and take note of the values in the "method" column (especially for customXML\item.xml).
Compare that value to a working docx - is it the same or different? Method "Deflate" works.
I faced the same issue and it turned out it was due to encoding.
Do you already specify the same encoding when serializing/deserializing?
Couple of suggestion
a. Try doc.Package.Flush(); after you write the data back into the custom xml.
b. You may have to delete all custom part and add a new custom part. We are using the following code and it seems working fine.
public static void ReplaceCustomXML(WordprocessingDocument myDoc, string customXML)
{
MainDocumentPart mainPart = myDoc.MainDocumentPart;
mainPart.DeleteParts<CustomXmlPart>(mainPart.CustomXmlParts);
CustomXmlPart customXmlPart = mainPart.AddCustomXmlPart(CustomXmlPartType.CustomXml);
using (StreamWriter ts = new StreamWriter(customXmlPart.GetStream()))
{
ts.Write(customXML);
ts.Flush();
ts.Close();
}
}
public static MemoryStream GetCustomXmlPart(MainDocumentPart mainPart)
{
foreach (CustomXmlPart part in mainPart.CustomXmlParts)
{
using (XmlTextReader reader =
new XmlTextReader(part.GetStream(FileMode.Open, FileAccess.Read)))
{
reader.MoveToContent();
if (reader.Name.Equals("aaaa", StringComparison.OrdinalIgnoreCase))
{
string str = reader.ReadOuterXml();
byte[] byteArray = Encoding.ASCII.GetBytes(str);
MemoryStream stream = new MemoryStream(byteArray);
return stream;
}
}
}
return null; //result;
}
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(ms, true))
{
StreamReader reader = new StreamReader(memStream);
string FullXML = reader.ReadToEnd();
ReplaceCustomXML(myDoc, FullXML);
myDoc.Package.Flush();
//Code to save file
}