Got a message " MEMORY STREAM IS NOT EXPANDABLE" after using WordprocessingDocument base on Microsoft site on MVC - c#

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

asp.net Web API csv helper writing to the stream doesn't work

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();
}
}

Return byte array of openxml document

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);
}

Method that return a string with an specifc extension

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.

Way of hiding important XML files using C# XNA

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");
}

Editing custom XML part in word document sometimes corrupts document

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
}

Categories

Resources