Hi I have a need to store some data in a excel file (.xlsx) so I can send the file to our customers and extract data from it again later when they send us the file back to update some content.
The customer should not be able to see the data we store in the file, and certainly not be able to remove it (accidentally). In fact he should not be aware of it in any way. Also we want to add this info from a service on a system that doesn't have Excel installed.
I know that a .xlsx file is basicaly a zip file so I can extract the data and add a file to it, zip it again and have a valid file that can be opened by Excel. Only problem here is that after saving that file in Excel my custom xml file is removed from the package. So I need to know how to fix this.
What I have:
XNamespace MyNamespace = "http://stackoverflow.com/questions/ask";
XNamespace ExcelNamespace = "http://schemas.openxmlformats.org/package/2006/relationships";
string ExtractionPath = #"C:\temp\test\";
string ExcelFile = #"C:\temp\example.xlsx";
Directory.CreateDirectory(ExtractionPath);
System.IO.Compression.ZipFile.ExtractToDirectory(ExcelFile, ExtractionPath);
var Root = new XElement(MyNamespace + "tracker",
new XAttribute("version", "1.0.0.1"),
new XElement("connections"));
var file = Path.Combine(ExtractionPath, "connectioninfo.xml");
if (!File.Exists(file))
{
var relsPath = Path.Combine(ExtractionPath, "_rels", ".rels");
var rels = XElement.Load(relsPath);
rels.Add(new XElement(ExcelNamespace + "Relationship",
new XAttribute("Id", "XXXX"),
new XAttribute("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml"),
new XAttribute("Target", "connectioninfo.xml")));
rels.Save(relsPath);
}
Root.Save(file);
if (File.Exists(ExcelFile)) File.Delete(ExcelFile);
System.IO.Compression.ZipFile.CreateFromDirectory(ExtractionPath, ExcelFile, System.IO.Compression.CompressionLevel.NoCompression, false);
When I run this code I end up with a file that contains my connectioninfo.xml file and that I can open in Excel. But when I save that file in excel and unzip the package again, then the connectioninfo.xml file is gone.
So question -> what am I missing to keep the file in the package after saving?
PS: I have also tried following code, but same problem ...
(using System.IO.Packaging;)
using (Package package = Package.Open(ExcelFile, FileMode.Open, FileAccess.ReadWrite))
{
Uri uriPartTarget = new Uri("/customXml/example.xml", UriKind.Relative);
if (!package.PartExists(uriPartTarget))
{
PackagePart customXml = package.CreatePart(uriPartTarget,
"application/vnd.openxmlformats-officedocument.customXmlProperties+xml");
using (Stream partStream = customXml.GetStream(FileMode.Create,
FileAccess.ReadWrite))
{
var doc = new XElement("test", new XElement("content","Hello world!"));
doc.Save(partStream);
}
}
}
Related
I want to Create a Microsoft Word Document in my UWP App and I've successfully created doc file in APP Local folder. But i want to create this doc file in a specific folder location chosen by user.
I mean user browse a location and then create doc file into that
location.
Here is my code to create Doc file in App local Path:
string docFileName = "TestDoc.docx";
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
string Filepath = localFolder.Path + "\\" + docFileName;
using (var wordprocessingDocument = WordprocessingDocument.Create(file.Path, DocumentFormat.OpenXml.WordprocessingDocumentType.Document))
{
MainDocumentPart mainPart = wordprocessingDocument.AddMainDocumentPart();
mainPart.Document = new DocumentFormat.OpenXml.Wordprocessing.Document();
Body body = mainPart.Document.AppendChild(new Body());
DocumentFormat.OpenXml.Wordprocessing.Paragraph para = body.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Paragraph());
DocumentFormat.OpenXml.Wordprocessing.Run run = para.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Run());
run.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Text("Hello !!! ."));
wordprocessingDocument.MainDocumentPart.Document.Save();
}
I've got below error while trying to create doc file in a specific location like below:
Error
Message=The media is write protected : 'D:\Training\TestDoc.docx'
Code
string path = #"D:\Training\TestDoc.docx";
using (var wordprocessingDocument = WordprocessingDocument.Create(path, DocumentFormat.OpenXml.WordprocessingDocumentType.Document))
{
MainDocumentPart mainPart = wordprocessingDocument.AddMainDocumentPart();
mainPart.Document = new DocumentFormat.OpenXml.Wordprocessing.Document();
Body body = mainPart.Document.AppendChild(new Body());
DocumentFormat.OpenXml.Wordprocessing.Paragraph para = body.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Paragraph());
DocumentFormat.OpenXml.Wordprocessing.Run run = para.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Run());
run.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Text("Hello !!! ."));
wordprocessingDocument.MainDocumentPart.Document.Save();
}
Because UWP apps run in a sandbox, you cannot directly access filesystem paths using classic APIs. However, you can use Windows.Storage APIs to do so.
Let user choose the save file
If you want to let the user choose the save file, you can use the FileSavePicker API. This Docs page describes this in detail. Once done, the API will give you a StorageFile instance, which you can use.
Write in the file
As you cannot use the Create method which takes a file path as an argument, you need to use the Stream based one instead.
To get a Stream instance from the file, add using System.IO; to the top of your code file and then do:
using(var stream = await file.OpenStreamForWriteAsync())
With this stream you can then do:
using (var wordprocessingDocument =
WordprocessingDocument.Create(
stream, WordprocessingDocumentType.Document))
{
//... your code
}
Note: Broad filesystem access
If you really need to create the file at an arbitrary (not user selected) location, you can use the broadFileSystemAccess capability. This is however only for apps that really require it. Note, that even with this capability, you still need to perform all file operations using the StorageFile and StorageFolder APIs.
I´m coding in C# and I have a project that the user will
be able to upload a .zip file and in that file will a .xml be read
and chapters based on xml tags will de displayed dynamic.
Right now it is hardcoded a specific file and in that file
that specific .xml file.
How do I read .xml file from a zip file and display that dynamic in C#?
Microsoft documentation has an exemple on this. Extracting specific file from a zip Archive and unzip them in a directory:
https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-compress-and-extract-files#example-2-extract-specific-file-extensions.
Then you next step will be to process the all or some file from the directory, https://learn.microsoft.com/en-us/dotnet/api/system.io.directory.getfiles?view=netframework-4.7.2#System_IO_Directory_GetFiles_System_String_System_String_
If you don't want to unzip, you can directly open the ZipArchiveEntry, with : https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.ziparchiveentry.open?view=netframework-4.7.2
With multiple Xml files in the zip all serialisation of myTypethe codes should boil down to :
string zipPath = #".\result.zip";
List<myType> listResults ;
using (ZipArchive archive = ZipFile.OpenRead(zipPath))
{
XmlSerializer serializer = new XmlSerializer(typeof(myType);
listResults =
archive
.Entries
.Where(entry => entry.FullName
.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
.Select(entry => (myType)serializer.Deserialize(entry.Open()))
.ToList();
}
For any missing reference in your project follow the light bulb 💡!
Add the System.IO.Compression.FileSystem assembly to your project references. Once you did that, you can open an archive like this:
static void Main(string[] args)
{
var zipPath = "Path-To-Your-Zipfile";
using (var archive = ZipFile.OpenRead(zipPath))
{
var xmlFile = archive.Entries.FirstOrDefault(e => e.FullName.EndsWith(".xml"));
if(xmlFile == null)
return;
using (var stream = xmlFile.Open())
{
using (var reader = new StreamReader(stream))
{
var fileContents = reader.ReadToEnd();
}
}
}
}
I am working on a code to export CV profile data to a Word document, following a template.
I am using C# and OpenXML.
I load the template-file to a MemoryStream, then make a new WordprocessingDocument from that.
The problem occours when I save this document to a file.
The resulting document can't be opened in neither MS Word nor LibreOffice Writer, giving me a general input/output error.
So I diabled the code I am testing for inserting the data, meaning the document I create should be an exact copy of the template. But no. No diffrence.
I had a closer look at the files, and unpacked both the template and the generated file, and ran them though WinMerge.
The structure inside the files are quite diffrent from each other.
Relevant part of the source-code:
static void Main(string[] args)
{
//force Visual studio to display errors in english (damned localized default settings)
if (Debugger.IsAttached)
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo("en-US");
Console.WriteLine("Hello world");
string pathData = "C:/Users/Lars Erik/Desktop/oppdrag fra synnes/oc-ny.json";
string pathTemplate = "C:/Users/Lars Erik/Desktop/oppdrag fra synnes/test_template/test_template.docx";
if (!File.Exists(pathData))
{
WriteLine("The path for the data is not valid. File not found. Path: " + pathData);
//return;
}
else if (!File.Exists(pathTemplate))
{
WriteLine("The path for the template is not valid. File not found. Path: " + pathTemplate);
//return;
}
//string rawData = File.ReadAllText(pathData);
FileStream Stream = new FileStream(pathData, FileMode.Open);
Byte[] templateRawdata = File.ReadAllBytes(pathTemplate);
MemoryStream outputStream = new MemoryStream();
outputStream.Write(templateRawdata, 0, templateRawdata.Length);
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Consultant));
Consultant data = serializer.ReadObject(Stream) as Consultant;
//JsonSerializerSettings settings = new JsonSerializerSettings();
//Object data = JsonConvert.DeserializeObject(rawData);
//Consultant data = JsonConvert.DeserializeObject<Consultant>(rawData);
//WriteLine(data.ToString());
WordprocessingDocument template = WordprocessingDocument.Open(pathTemplate, false);
WordprocessingDocument outputdoc = WordprocessingDocument.Open(outputStream, true);
//TranslateCV(data, template, ref outputdoc);
//outputdoc.Close();
outputStream.CopyTo(File.Create("C:/Users/Lars Erik/Desktop/oppdrag fra synnes/OUTPUT/testresult.docx"));
}
Here is some screenshots from WinMerge comparing the should-be identical template and resulting document:
(The UI is in Norwegian. Not sure if I can change that)
Following is the ActionResult Method created to return EXCEl File, which works fine. However, there is a problem that the content for each column is too large that the rows are mal-formed, which means there are wide blank-spaces between rows.
Following is the ActionResult code.
public ActionResult ImportExecelFile(int appNo)
{
List<PendingApproval> pendings = objPA.GetPendingApprovals(appNo.ToString());
string xml = String.Empty;
XmlDocument xmlDoc = new XmlDocument();
XmlSerializer xmlSerializer = new XmlSerializer(pendings.GetType());
using (MemoryStream xmlStream = new MemoryStream())
{
xmlSerializer.Serialize(xmlStream, pendings);
xmlStream.Position = 0;
xmlDoc.Load(xmlStream);
xml = xmlDoc.InnerXml;
}
//Create file
string fileName = "Pending_Approvals";
fileName += string.Format("-{0}", DateTime.Now.ToString("yyyy-MM-dd"));
fileName += ".xls";
byte[] fileContents = Encoding.UTF8.GetBytes(xml);
return File(fileContents, "application/vnd.ms-excel", fileName);
}
You're not creating an Excel file - you're creating XML and saving it with an .xls extension. Excel will try its best to open it, although it's probably going to raise a warning to the user because it's not really an Excel file.
Excel files can contain formatting, including column widths. Data serialized as XML will not have that. So there's really no way to control how Excel displays the XML.
You can accomplish what you're trying to do using a library like EPPlus that creates Excel workbooks using OpenXML. But if you're just saving XML as .xls then you won't have any control over formatting. (I'm amazed it opens at all, but I tested saving an XML file as .xls and it worked except for the warning message.)
In C# am trying to check to see if an XML file is created, if not create the file and then create the xml declaration, a comment and a parent node.
When I try to load it, it gives me this error:
"The process cannot access the file 'C:\FileMoveResults\Applications.xml' because it is being used by another process."
I checked the task manager to ensure it wasn't open and sure enough there were no open applications of it. Any ideas of what's going on?
Here is the code I am using:
//check for the xml file
if (!File.Exists(GlobalVars.strXMLPath))
{
//create the xml file
File.Create(GlobalVars.strXMLPath);
//create the structure
XmlDocument doc = new XmlDocument();
doc.Load(GlobalVars.strXMLPath);
//create the xml declaration
XmlDeclaration xdec = doc.CreateXmlDeclaration("1.0", null, null);
//create the comment
XmlComment xcom = doc.CreateComment("This file contains all the apps, versions, source and destination paths.");
//create the application parent node
XmlNode newApp = doc.CreateElement("applications");
//save
doc.Save(GlobalVars.strXMLPath);
Here is the code I ended up using to fix this issue:
//check for the xml file
if (!File.Exists(GlobalVars.strXMLPath))
{
using (XmlWriter xWriter = XmlWriter.Create(GlobalVars.strXMLPath))
{
xWriter.WriteStartDocument();
xWriter.WriteComment("This file contains all the apps, versions, source and destination paths.");
xWriter.WriteStartElement("application");
xWriter.WriteFullEndElement();
xWriter.WriteEndDocument();
}
File.Create() returns a FileStream that locks the file until it's closed.
You don't need to call File.Create() at all; doc.Save() will create or overwrite the file.
I would suggest something like this:
string filePath = "C:/myFilePath";
XmlDocument doc = new XmlDocument();
if (System.IO.File.Exists(filePath))
{
doc.Load(filePath);
}
else
{
using (XmlWriter xWriter = XmlWriter.Create(filePath))
{
xWriter.WriteStartDocument();
xWriter.WriteStartElement("Element Name");
xWriter.WriteEndElement();
xWriter.WriteEndDocument();
}
//OR
XmlDeclaration xdec = doc.CreateXmlDeclaration("1.0", null, null);
XmlComment xcom = doc.CreateComment("This file contains all the apps, versions, source and destination paths.");
XmlNode newApp = doc.CreateElement("applications");
XmlNode newApp = doc.CreateElement("applications1");
XmlNode newApp = doc.CreateElement("applications2");
doc.Save(filePath); //save a copy
}
The reason your code is currently having problems is because of: File.Create creates the file and opens the stream to the file, and then you never make use of it (never close it) on this line:
//create the xml file
File.Create(GlobalVars.strXMLPath);
if you did something like
//create the xml file
using(Stream fStream = File.Create(GlobalVars.strXMLPath)) { }
Then you would not get that in use exception.
As a side note XmlDocument.Load will not create a file, only work with an already create one
You could create a stream, setting the FileMode to FileMode.Create and then use the stream to save the Xml to the path specified.
using (System.IO.Stream stream = new System.IO.FileStream(GlobalVars.strXMLPath, FileMode.Create))
{
XmlDocument doc = new XmlDocument();
...
doc.Save(stream);
}