I'm using a Dataset.Load statement to load a XMl File and on the file I have some tags with the "&" character and this is causing a exception. Are there any way to Load the XML to the dataset or replacing the & for another string.
I tried to do a Replace but when I use StringVar.Replace("&","e") for example when I have "ç" or "ã" strings on the file this chars are replaced for an wrong sequence of chars.
I was trying this
My.Computer.FileSystem.WriteAllText(MyFilePath, My.Computer.FileSystem.ReadAllText(MyFilePath, System.Text.Encoding.UTF8).Replace(" & ", "&"), False, System.Text.Encoding.UTF8)
but it happens that some files has "A&B" or any other combination of letters before and after the "&"
I'll be glad if anyone can help-me.
Thanks
`Hello Guys, I solved my problem. The problem was really #Blorgbeard sayd the Xml File was coming not valid.
Public Shared Function Decompress(text As String) As String
Dim bytes As Byte() = Convert.FromBase64String(text)
Using msi = New MemoryStream(bytes)
Using mso = New MemoryStream()
Using gs = New System.IO.Compression.GZipStream(msi, System.IO.Compression.CompressionMode.Decompress)
Dim bytesAux As Byte() = New Byte(4095) {}
Dim cnt As Integer
While (InlineAssignHelper(cnt, gs.Read(bytesAux, 0, bytesAux.Length))) <> 0
mso.Write(bytesAux, 0, cnt)
End While
End Using
Dim streamReader As StreamReader = New StreamReader(mso, System.Text.Encoding.UTF8, True)
Dim XmlDoc As String
mso.Seek(0, SeekOrigin.Begin)
XmlDoc = streamReader.ReadToEnd
Return XmlDoc
End Using
End Using
End Function`
this is what I did to get and return the string containing the correct XML data to be write to file.
Related
I want to copy the content of a document created by the user to an existing document. The existing document content must be an exact mirror to the document created by the user.
I cannot simply copy the file using System.IO or saving a copy of the document created by the user using SaveAs methods in Word Interop. This is because the existing document is a document that is generated from a webserver and has VBA modules for uploading it back to the server.
The document generated by the webserver (existing document) is a Word 2003 document, but the document created by the user is either a Word 2003 document or Word 2007+.
Having these limitations in mind, I first created the following method:
string tempsave = //location of user created document;
string savelocation = //location of existing document;
Word.Application objWordOpen = new Word.Application();
Document doclocal = objWordOpen.Documents.Open(tempsave);
Document d1 = objWordOpen.Documents.Open(savelocation);
Word.Range oRange = doclocal.Content;
oRange.Copy();
d1.Activate();
d1.UpdateStyles();
d1.ActiveWindow.Selection.WholeStory();
d1.ActiveWindow.Selection.PasteAndFormat(Word.WdRecoveryType.wdFormatOriginalFormatting);
This is generally working. However, the tables are messed up.
Also, if there is a Page Break, the output is different.
The user created document:
The output - existing document:
Also, at the end of the document a paragraph mark is added, as follows:
The user created document:
The output - existing document:
The page format is also messed up, the output having mirror margins set up.
The user created document:
The output - existing document:
I have also tried using Range.Insert() method and setting the range without copying as described here https://stackoverflow.com/a/54500605/10468231, but I am still having these issues.
I have also tried adding the VBA modules to the document, but there are also Document Variables and other custom properties and I don't want to mess with the file being uploaded to the server.
How do I handle these issues? Both the documents are based on Normal template.
I am open to another suggestion regarding this topic, but I know that .doc files are not handled as easily as .docx format, this is why I think I am stuck with COM Interop.
Thank you.
UPDATE
Based on Macropod code posted by Charles Kenyon, I have managed to copy more of the formatting from the source to target. Still, there is the difference at the page break - the paragraph mark is places on the new page, instead on the same page.
Also, the text is slightly larger, even though the Font Size is the same.
Word.Range oRange;
oRange = Source.Content;
Target.Content.FormattedText = oRange.FormattedText;
LayoutTransfer(Source, Target);
LayoutTransfer method:
private void LayoutTransfer(Document source, Document target)
{
float sPageHght;
float sPageWdth;
float sHeaderDist;
float sFooterDist;
float sTMargin;
float sBMargin;
float sLMargin;
float sRMargin;
float sGutter;
WdGutterStyle sGutterPos;
WdPaperSize lPaperSize;
WdGutterStyleOld lGutterStyle;
int lMirrorMargins;
WdVerticalAlignment lVerticalAlignment;
WdSectionStart lScnStart;
WdSectionDirection lScnDir;
int lOddEvenHdFt;
int lDiffFirstHdFt;
bool bTwoPagesOnOne;
bool bBkFldPrnt;
int bBkFldPrnShts;
bool bBkFldRevPrnt;
WdOrientation lOrientation;
foreach (Word.Section section in source.Sections)
{
lPaperSize = section.PageSetup.PaperSize;
lGutterStyle = section.PageSetup.GutterStyle;
lOrientation = section.PageSetup.Orientation;
lMirrorMargins = section.PageSetup.MirrorMargins;
lScnStart = section.PageSetup.SectionStart;
lScnDir = section.PageSetup.SectionDirection;
lOddEvenHdFt = section.PageSetup.OddAndEvenPagesHeaderFooter;
lDiffFirstHdFt = section.PageSetup.DifferentFirstPageHeaderFooter;
lVerticalAlignment = section.PageSetup.VerticalAlignment;
sPageHght = section.PageSetup.PageHeight;
sPageWdth = section.PageSetup.PageWidth;
sTMargin = section.PageSetup.TopMargin;
sBMargin = section.PageSetup.BottomMargin;
sLMargin = section.PageSetup.LeftMargin;
sRMargin = section.PageSetup.RightMargin;
sGutter = section.PageSetup.Gutter;
sGutterPos = section.PageSetup.GutterPos;
sHeaderDist = section.PageSetup.HeaderDistance;
sFooterDist = section.PageSetup.FooterDistance;
bTwoPagesOnOne = section.PageSetup.TwoPagesOnOne;
bBkFldPrnt = section.PageSetup.BookFoldPrinting;
bBkFldPrnShts = section.PageSetup.BookFoldPrintingSheets;
bBkFldRevPrnt = section.PageSetup.BookFoldRevPrinting;
var index = section.Index;
target.Sections[index].PageSetup.PaperSize = lPaperSize;
target.Sections[index].PageSetup.GutterStyle = lGutterStyle;
target.Sections[index].PageSetup.Orientation = lOrientation;
target.Sections[index].PageSetup.MirrorMargins = lMirrorMargins;
target.Sections[index].PageSetup.SectionStart = lScnStart;
target.Sections[index].PageSetup.SectionDirection = lScnDir;
target.Sections[index].PageSetup.OddAndEvenPagesHeaderFooter = lOddEvenHdFt;
target.Sections[index].PageSetup.DifferentFirstPageHeaderFooter = lDiffFirstHdFt;
target.Sections[index].PageSetup.VerticalAlignment = lVerticalAlignment;
target.Sections[index].PageSetup.PageHeight = sPageHght;
target.Sections[index].PageSetup.PageWidth = sPageWdth;
target.Sections[index].PageSetup.TopMargin = sTMargin;
target.Sections[index].PageSetup.BottomMargin = sBMargin;
target.Sections[index].PageSetup.LeftMargin = sLMargin;
target.Sections[index].PageSetup.RightMargin = sRMargin;
target.Sections[index].PageSetup.Gutter = sGutter;
target.Sections[index].PageSetup.GutterPos = sGutterPos;
target.Sections[index].PageSetup.HeaderDistance = sHeaderDist;
target.Sections[index].PageSetup.FooterDistance = sFooterDist;
target.Sections[index].PageSetup.TwoPagesOnOne = bTwoPagesOnOne;
target.Sections[index].PageSetup.BookFoldPrinting = bBkFldPrnt;
target.Sections[index].PageSetup.BookFoldPrintingSheets = bBkFldPrnShts;
target.Sections[index].PageSetup.BookFoldRevPrinting = bBkFldRevPrnt;
}
}
UPDATE 2
Actually, the page break not remaining in line with paragraph format is not an issue of copying fidelity, but rather an issue of conversion from .doc to .docx. (https://support.microsoft.com/en-us/help/923183/the-layout-of-a-document-that-contains-a-page-break-may-be-different-i)
Maybe someone thought of a method to overcome this.
The following code by Paul Edstein (macropod) may assist you. It will at least give you an idea of the complexities you are facing.
' ============================================================================================================
' KEEP NEXT THREE TOGETHER
' ============================================================================================================
'
Sub CombineDocuments()
' Paul Edstein
' https://www.msofficeforums.com/word-vba/43339-combine-multiple-word-documents.html
'
' Users occasionally need to combine multiple documents that may of may not have the same page layouts,
' Style definitions, and so on. Consequently, combining multiple documents is often rather more complex than
' simply copying & pasting content from one document to another. Problems arise when the documents have
' different page layouts, headers, footers, page numbering, bookmarks & cross-references,
' Tables of Contents, Indexes, etc., etc., and especially when those documents have used the same Style
' names with different definitions.
'
' The following Word macro (for Windows PCs only) handles the more common issues that arise when combining
' documents; it does not attempt to resolve conflicts with paragraph auto-numbering,
' document -vs- section page numbering in 'page x of y' numbering schemes, Tables of Contents or Indexing issues.
' Neither does it attempt to deal with the effects on footnote or endnote numbering & positioning or with the
' consequences of duplicated bookmarks (only one of which can exist in the merged document) and any corresponding
' cross-references.
'
' The macro includes a folder browser. Simply select the folder to process and all documents in that folder
' will be combined into the currently-active document. Word's .doc, .docx, and .docm formats will all be processed,
' even if different formats exist in the selected folder.
'
Application.ScreenUpdating = False
Dim strFolder As String, strFile As String, strTgt As String
Dim wdDocTgt As Document, wdDocSrc As Document, HdFt As HeaderFooter
strFolder = GetFolder: If strFolder = "" Then Exit Sub
Set wdDocTgt = ActiveDocument: strTgt = ActiveDocument.fullname
strFile = Dir(strFolder & "\*.doc", vbNormal)
While strFile <> ""
If strFolder & strFile <> strTgt Then
Set wdDocSrc = Documents.Open(FileName:=strFolder & "\" & strFile, AddToRecentFiles:=False, Visible:=False)
With wdDocTgt
.Characters.Last.InsertBefore vbCr
.Characters.Last.InsertBreak (wdSectionBreakNextPage)
With .Sections.Last
For Each HdFt In .Headers
With HdFt
.LinkToPrevious = False
.range.Text = vbNullString
.PageNumbers.RestartNumberingAtSection = True
.PageNumbers.StartingNumber = wdDocSrc.Sections.First.Headers(HdFt.Index).PageNumbers.StartingNumber
End With
Next
For Each HdFt In .Footers
With HdFt
.LinkToPrevious = False
.range.Text = vbNullString
.PageNumbers.RestartNumberingAtSection = True
.PageNumbers.StartingNumber = wdDocSrc.Sections.First.Headers(HdFt.Index).PageNumbers.StartingNumber
End With
Next
End With
Call LayoutTransfer(wdDocTgt, wdDocSrc)
.range.Characters.Last.FormattedText = wdDocSrc.range.FormattedText
With .Sections.Last
For Each HdFt In .Headers
With HdFt
.range.FormattedText = wdDocSrc.Sections.Last.Headers(.Index).range.FormattedText
.range.Characters.Last.Delete
End With
Next
For Each HdFt In .Footers
With HdFt
.range.FormattedText = wdDocSrc.Sections.Last.Footers(.Index).range.FormattedText
.range.Characters.Last.Delete
End With
Next
End With
End With
wdDocSrc.Close SaveChanges:=False
End If
strFile = Dir()
Wend
With wdDocTgt
' Save & close the combined document
.SaveAs FileName:=strFolder & "Forms.docx", FileFormat:=wdFormatXMLDocument, AddToRecentFiles:=False
' and/or:
.SaveAs FileName:=strFolder & "Forms.pdf", FileFormat:=wdFormatPDF, AddToRecentFiles:=False
.Close SaveChanges:=False
End With
Set wdDocSrc = Nothing: Set wdDocTgt = Nothing
Application.ScreenUpdating = True
End Sub
' ============================================================================================================
Private Function GetFolder() As String
' used by CombineDocument macro by Paul Edstein, keep together in same module
' https://www.msofficeforums.com/word-vba/43339-combine-multiple-word-documents.html
Dim oFolder As Object
GetFolder = ""
Set oFolder = CreateObject("Shell.Application").BrowseForFolder(0, "Choose a folder", 0)
If (Not oFolder Is Nothing) Then GetFolder = oFolder.Items.Item.Path
Set oFolder = Nothing
End Function
Sub LayoutTransfer(wdDocTgt As Document, wdDocSrc As Document)
' works with previous Combine Documents macro from Paul Edstein, keep together
' https://www.msofficeforums.com/word-vba/43339-combine-multiple-word-documents.html
'
Dim sPageHght As Single, sPageWdth As Single
Dim sHeaderDist As Single, sFooterDist As Single
Dim sTMargin As Single, sBMargin As Single
Dim sLMargin As Single, sRMargin As Single
Dim sGutter As Single, sGutterPos As Single
Dim lPaperSize As Long, lGutterStyle As Long
Dim lMirrorMargins As Long, lVerticalAlignment As Long
Dim lScnStart As Long, lScnDir As Long
Dim lOddEvenHdFt As Long, lDiffFirstHdFt As Long
Dim bTwoPagesOnOne As Boolean, bBkFldPrnt As Boolean
Dim bBkFldPrnShts As Boolean, bBkFldRevPrnt As Boolean
Dim lOrientation As Long
With wdDocSrc.Sections.Last.PageSetup
lPaperSize = .PaperSize
lGutterStyle = .GutterStyle
lOrientation = .Orientation
lMirrorMargins = .MirrorMargins
lScnStart = .SectionStart
lScnDir = .SectionDirection
lOddEvenHdFt = .OddAndEvenPagesHeaderFooter
lDiffFirstHdFt = .DifferentFirstPageHeaderFooter
lVerticalAlignment = .VerticalAlignment
sPageHght = .PageHeight
sPageWdth = .PageWidth
sTMargin = .TopMargin
sBMargin = .BottomMargin
sLMargin = .LeftMargin
sRMargin = .RightMargin
sGutter = .Gutter
sGutterPos = .GutterPos
sHeaderDist = .HeaderDistance
sFooterDist = .FooterDistance
bTwoPagesOnOne = .TwoPagesOnOne
bBkFldPrnt = .BookFoldPrinting
bBkFldPrnShts = .BookFoldPrintingSheets
bBkFldRevPrnt = .BookFoldRevPrinting
End With
With wdDocTgt.Sections.Last.PageSetup
.GutterStyle = lGutterStyle
.MirrorMargins = lMirrorMargins
.SectionStart = lScnStart
.SectionDirection = lScnDir
.OddAndEvenPagesHeaderFooter = lOddEvenHdFt
.DifferentFirstPageHeaderFooter = lDiffFirstHdFt
.VerticalAlignment = lVerticalAlignment
.PageHeight = sPageHght
.PageWidth = sPageWdth
.TopMargin = sTMargin
.BottomMargin = sBMargin
.LeftMargin = sLMargin
.RightMargin = sRMargin
.Gutter = sGutter
.GutterPos = sGutterPos
.HeaderDistance = sHeaderDist
.FooterDistance = sFooterDist
.TwoPagesOnOne = bTwoPagesOnOne
.BookFoldPrinting = bBkFldPrnt
.BookFoldPrintingSheets = bBkFldPrnShts
.BookFoldRevPrinting = bBkFldRevPrnt
.PaperSize = lPaperSize
.Orientation = lOrientation
End With
End Sub
' ============================================================================================================
I used a Template and copied it several times into a new Word Document after editing it.
It worked like this
Word.Range rng = wordDocTarget.Content;
rng.Collapse(Word.WdCollapseDirection.wdCollapseEnd)
rng.FormattedText = wordDocSource.Content.FormattedText
An alternative could also be to insert a whole file to a range / document
rng = wordDoc.Range
rng.Collapse(Word.WdCollapseDirection.wdCollapseEnd)
rng.InsertFile(filepath)
In .NET, I'm trying to use Encoding.UTF8.GetString method, which takes a byte array and converts it to a string.
It looks like this method ignores the BOM (Byte Order Mark), which might be a part of a legitimate binary representation of a UTF8 string, and takes it as a character.
I know I can use a TextReader to digest the BOM as needed, but I thought that the GetString method should be some kind of a macro that makes our code shorter.
Am I missing something? Is this like so intentionally?
Here's a reproduction code:
static void Main(string[] args)
{
string s1 = "abc";
byte[] abcWithBom;
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms, new UTF8Encoding(true)))
{
sw.Write(s1);
sw.Flush();
abcWithBom = ms.ToArray();
Console.WriteLine(FormatArray(abcWithBom)); // ef, bb, bf, 61, 62, 63
}
byte[] abcWithoutBom;
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms, new UTF8Encoding(false)))
{
sw.Write(s1);
sw.Flush();
abcWithoutBom = ms.ToArray();
Console.WriteLine(FormatArray(abcWithoutBom)); // 61, 62, 63
}
var restore1 = Encoding.UTF8.GetString(abcWithoutBom);
Console.WriteLine(restore1.Length); // 3
Console.WriteLine(restore1); // abc
var restore2 = Encoding.UTF8.GetString(abcWithBom);
Console.WriteLine(restore2.Length); // 4 (!)
Console.WriteLine(restore2); // ?abc
}
private static string FormatArray(byte[] bytes1)
{
return string.Join(", ", from b in bytes1 select b.ToString("x"));
}
It looks like this method ignores the BOM (Byte Order Mark), which might be a part of a legitimate binary representation of a UTF8 string, and takes it as a character.
It doesn't look like it "ignores" it at all - it faithfully converts it to the BOM character. That's what it is, after all.
If you want to make your code ignore the BOM in any string it converts, that's up to you to do... or use StreamReader.
Note that if you either use Encoding.GetBytes followed by Encoding.GetString or use StreamWriter followed by StreamReader, both forms will either produce then swallow or not produce the BOM. It's only when you mix using a StreamWriter (which uses Encoding.GetPreamble) with a direct Encoding.GetString call that you end up with the "extra" character.
Based on the answer by Jon Skeet (thanks!), this is how I just did it:
var memoryStream = new MemoryStream(byteArray);
var s = new StreamReader(memoryStream).ReadToEnd();
Note that this will probably only work reliably if there is a BOM in the byte array you are reading from. If not, you might want to look into another StreamReader constructor overload which takes an Encoding parameter so you can tell it what the byte array contains.
for those who do not want to use streams I found a quite simple solution using Linq:
public static string GetStringExcludeBOMPreamble(this Encoding encoding, byte[] bytes)
{
var preamble = encoding.GetPreamble();
if (preamble?.Length > 0 && bytes.Length >= preamble.Length && bytes.Take(preamble.Length).SequenceEqual(preamble))
{
return encoding.GetString(bytes, preamble.Length, bytes.Length - preamble.Length);
}
else
{
return encoding.GetString(bytes);
}
}
I know I am kind of late to the party but here's the code I am using (feel free to adapt to C#) if you need:
Public Function Serialize(Of YourXMLClass)(ByVal obj As YourXMLClass,
Optional ByVal omitXMLDeclaration As Boolean = True,
Optional ByVal omitXMLNamespace As Boolean = True) As String
Dim serializer As New XmlSerializer(obj.GetType)
Using memStream As New MemoryStream()
Dim settings As New XmlWriterSettings() With {
.Encoding = Encoding.UTF8,
.Indent = True,
.omitXMLDeclaration = omitXMLDeclaration}
Using writer As XmlWriter = XmlWriter.Create(memStream, settings)
Dim xns As New XmlSerializerNamespaces
If (omitXMLNamespace) Then xns.Add("", "")
serializer.Serialize(writer, obj, xns)
End Using
Return Encoding.UTF8.GetString(memStream.ToArray())
End Using
End Function
Public Function Deserialize(Of YourXMLClass)(ByVal obj As YourXMLClass, ByVal xml As String) As YourXMLClass
Dim result As YourXMLClass
Dim serializer As New XmlSerializer(GetType(YourXMLClass))
Using memStream As New MemoryStream()
Dim bytes As Byte() = Encoding.UTF8.GetBytes(xml.ToArray)
memStream.Write(bytes, 0, bytes.Count)
memStream.Seek(0, SeekOrigin.Begin)
Using reader As XmlReader = XmlReader.Create(memStream)
result = DirectCast(serializer.Deserialize(reader), YourXMLClass)
End Using
End Using
Return result
End Function
I have an existing asp page which largely can't/won't change which calls a service, sending an XML document.
Private Function QueryXYZ(ByVal strStreet1 As String, _
ByVal strStreet2 As String, _
ByVal strCity As String, _
ByVal strState As String, _
ByVal strZipMain As String, _
ByRef objDomDoc As DOMDocument, _
ByRef blnStreetMatch) As Boolean
On Error GoTo errorHandler
Dim intCount As Integer
Dim lngErrNum As Long
Dim objResult As IXMLDOMNode
Dim objResultSet As IXMLDOMNodeList
Dim objXMLHTTP As New ServerXMLHTTP
Dim strErrDesc As String
Dim strFault As String
Dim strMessage As String
Dim strResults As String
Dim strSoap As String
'CO 11784 - start
'Build Soap XML request
strSoap = _
"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance/' xmlns:xsd='http://www.w3.org/2001/XMLSchema/'>" & _
"<soap:Body>" & _
"<MatchAddress xmlns='http://(address/'>" & _
"<MatchParms>" & _
"<Firm />" & _
"<Street1>" & strStreet1 & "</Street1>" & _
"</MatchParms>" & _
"</MatchAddress>" & _
"</soap:Body>" & _
"</soap:Envelope>"
'CO 11784 - end
'Load Request into XML document
objDomDoc.async = False
objDomDoc.loadXML (strSoap)
'Check for syntax errors in Request
If objDomDoc.parseError.errorCode <> 0 Then
Err.Raise 10620, "Query", "Error parsing generated xml query: [" & objDomDoc.parseError.reason & _
"]" & "[" & objDomDoc.parseError.srcText & "]"
End If
'Send the Request
objXMLHTTP.Open "POST", mstrGISURL, False
objXMLHTTP.setRequestHeader "soapaction", "http://sampleaddress.com/MatchAddress"
objXMLHTTP.send objDomDoc
'Load Response
strResults = Replace(objXMLHTTP.responseText, """, """")
strResults = Replace(strResults, ">", ">")
strResults = Replace(strResults, "<", "<")
strResults = Replace(strResults, "'", "'")
objDomDoc.loadXML (strResults)
I want to set it to talk to a new (non-WCF) service I'm writing.
public XmlDocument Matchaddress(string AddressInXML)
(I know that parameter call is almost certainly wrong, it's set that way just to test it using the service.asmx form)
The problem is, I can't find where the XML is located in the request. I've checked the and the input stream with a test function:
System.IO.StreamReader sr = new System.IO.StreamReader(HttpContext.Current.Request.InputStream);
string requestContents = sr.ReadToEnd();
sr.Close();
StreamWriter Sw = System.IO.File.CreateText( #"C:\Temp\testfile.txt");
for (int i = 0; i < HttpContext.Current.Request.Headers.AllKeys.Length; i++)
{
Sw.WriteLine (HttpContext.Current.Request.Headers.AllKeys[i] + Environment.NewLine);
//if (HttpContext.Current.Request.Headers.AllKeys[i] == "SOAPAction")
//{
string soaphd = HttpContext.Current.Request.Headers.AllKeys[i];
string soapTXT = System.Web.HttpContext.Current.Request.Headers[soaphd];
Sw.WriteLine(soapTXT + Environment.NewLine);
//}
}
Sw.Close();
And can't find it.
I am clearly doing something wrong. I'm not sure if there's a change that needs making to that classic ASP code, no matter how hard it will be to do. I can't tell if I need to find the location and then change the input parameter (or remove it entirely) or if I need to know what the right parameter is first, and then the data will magically appear.
I don't know that much about that "soapaction" header - does it need to match the address of the new service, or can it be some generic (or just plain incorrect) other address? Same question for the
<MatchAddress xmlns='http://(address/'>"
line in the XML - could that be bollixing it up by not being a matching address?
I don't even see the XML when I try to run it in SOAPUI, so whether that means anything or not I dunno.
More details can be provided as requested.
Turns out I was close using the stream reader, I was just reading it into the wrong kind of object. This gives me access to the entire XML :
XmlDocument xdoc = new XmlDocument();
using (Stream receiveStream = HttpContext.Current.Request.InputStream)
{
// Move to begining of input stream and read
receiveStream.Position = 0;
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
// Load into XML document
xdoc.Load(readStream);
}
}
Dumping the resulting XML into a string allowed me to get at anything I wanted, any way I care to. No need to worry about the parameters in the function definition at all.
I am trying to create a csv file of some data. I have wrote a function that successfully does this....
Private Sub CreateCSVFile(ByVal dt As DataTable, ByVal strFilePath As String)
Dim sw As New StreamWriter(strFilePath, False)
''# First we will write the headers.
''#DataTable dt = m_dsProducts.Tables[0];
Dim iColCount As Integer = dt.Columns.Count
For i As Integer = 0 To iColCount - 1
sw.Write(dt.Columns(i))
If i < iColCount - 1 Then
sw.Write(",")
End If
Next
sw.Write(sw.NewLine)
''# Now write all the rows.
For Each dr As DataRow In dt.Rows
For i As Integer = 0 To iColCount - 1
If Not Convert.IsDBNull(dr(i)) Then
sw.Write(dr(i).ToString())
End If
If i < iColCount - 1 Then
sw.Write(",")
End If
Next
sw.Write(sw.NewLine)
Next
sw.Close()
End Sub
The problem is I am not using the streamwriter object correctly for what I trying to accomplish. Since this is an asp.net I need the user to pick a local filepath to put the file on. If I pass any path to this function its gonna try to write it to the directory specified on the server where the code is. I would like this to popup and let the user select a place on their local machine to put the file....
Dim exData As Byte() = File.ReadAllBytes(Server.MapPath(eio))
File.Delete(Server.MapPath(eio))
Response.AddHeader("content-disposition", String.Format("attachment; filename={0}", fn))
Response.ContentType = "application/x-msexcel"
Response.BinaryWrite(exData)
Response.Flush()
Response.End()
I am calling the first function in code like this...
Dim emplTable As DataTable = SiteAccess.DownloadEmployee_H()
CreateCSVFile(emplTable, "C:\\EmplTable.csv")
Where I dont want to have specify the file loaction (because this will put the file on the server and not on a client machine) but rather let the user select the location on their client machine.
Can someone help me put this together? Thanks in advance.
I have recreated my export function and now it lets the usr select a download path, but one column in the data being downloaded has data in the form of "Doe, John" this column is called "EPLNME" this messes up the output file because its reading the comma in the data and now the data is off by a column in the output file can someone help me stop this specific incident im not sure how I can. Here is the code...
Private Sub ExportCSV(ByVal data As DataTable, ByVal nameOfFile As String)
Dim context As HttpContext = HttpContext.Current
context.Response.Clear()
context.Response.ContentType = "text/csv"
context.Response.AddHeader("Content-Disposition", "attachment; filename=" + nameOfFile + ".csv")
''#Write column header names
For i = 0 To data.Columns.Count - 1
If (i > 0) Then
context.Response.Write(",")
End If
context.Response.Write(data.Columns(i).ColumnName)
Next
context.Response.Write(Environment.NewLine)
''#Write data
For Each row As DataRow In data.Rows
For i = 0 To data.Columns.Count - 1
If (i > 0) Then
context.Response.Write(",")
End If
context.Response.Write(row.Item(i).ToString())
Next
context.Response.Write(Environment.NewLine)
Next
context.Response.End()
End Sub
First, you need to overload your function like this, to allow sending your output directly to either a stream or a path:
Private Sub CreateCSVFile(ByVal dt As DataTable, ByVal strFilePath As String)
Using sw As New StreamWriter(strFilePath)
CreateCSVFile(dt, sw)
End Using
End Sub
Private Sub CreateCSVFile(ByVal dt As DataTable, ByVal outStream As TextWriter)
''# First we will write the headers.
Dim delimiter As String = String.Empty
For Each col As DataColumn in dt.Columns
outStream.Write(delimiter)
outStream.Write(col.ColumnName)
delimiter = ","
Next col
outStream.Write(outStream.NewLine)
int flushCount = 0;
''# Now write all the rows.
For Each dr As DataRow In dt.Rows
delimiter = String.Empty
For i As Integer = 0 To dt.Columns.Count -1
outStream.Write(delimiter)
If Not Convert.IsDBNull(dr(i)) Then
outStream.Write("""") ''#Wrap fields in quotes to allow for commas in field data
''# Need to escape the quotes as well
outStream.Write(dr(i).ToString().Replace("""", """"""))
outStream.Write("""")
End If
delimiter = ","
Next i
outStream.Write(outStream.NewLine)
''# Flush the buffer periodically
flushCount += 1
If flushCount > 100 Then
outStream.Flush()
flushCount = 0
End If
Next dr
End Sub
Notice that your function works pretty much exactly the same as before, but you can now write to a file or directly to a stream, and you didn't have to re-write a lot of code to make it work. Pretty much anything you write that works with files should be written this way. I made a few other improvements to the code as well, but the main thing is the method that does the actual work should always accept a TextWriter and then just add overloads if you want to be able to accept anything else like a file path.
Now what you can do is take the Content Type and Header from Ben Robinson's answer and use this new method to write directly to the asp.net response buffer:
Response.ContentType = "text/csv";
Response.AddHeader("Content-Disposition", "attachment; filename=NameOfFile");
CreateCSVFile(SiteAccess.DownloadEmployee_H(), Response.Output)
Response.Flush()
Response.End()
You, don't really nead a streamwriter, that is for creating files on the machine where the code is running. Use a StringBuilder to build up the string that represents the CSV file then do the following:
Response.ContentType = "text/csv";
Response.AddHeader("Content-Disposition", "attachment; filename=NameOfFile");
Response.Write(MyStringBuilder.ToString());
If you do need to create a file because you need to store it on the server and also transmit it to the user. Create the file as you are doing and the replace the last line with
Response.TransmitFile("filePath");
You can use a MemoryStream to hold the binary data on the server, instead of writing them to a file.
1) Write the contents youn want to put in the CSV into the memory stream
2) Read from the MemoryStream into the Response when required.
Hope it helps!
I need a regex to run against strings like the one below that will convert absolute paths to relative paths under certain conditions.
<p>This website is <strong>really great</strong> and people love it <img alt="" src="http://localhost:1379/Content/js/fckeditor/editor/images/smiley/msn/teeth_smile.gif" /></p>
Rules:
If the url contains "/Content/" I
would like to get the relative path
If the url does not contain
"/Content/", it is an external file,
and the absolute path should remain
Regex unfortunatley is not my forte, and this is too advanced for me at this point. If anyone can offer some tips I'd appreciate it.
Thanks in advance.
UPDATE:
To answer questions in the comments:
At the time the Regex is applied, All urls will begin with "http://"
This should be applied to the src attribute of both img and a tags, not to text outside of tags.
You should consider using the Uri.MakeRelativeUri method - your current algorithm depends on external files never containing "/Content/" in their path, which seems risky to me. MakeRelativeUri will determine whether a relative path can be made from the current Uri to the src or href regardless of changes you or the external file store make down the road.
Unless I'm missing the point here, if you replace
^(.*)([C|c]ontent.*)
With
/$2
You will end up with
/Content/js/fckeditor/editor/images/smiley/msn/teeth_smile.gif
This will only happen id "content" is found, so in cae you have a URL such as:
http://localhost:1379/js/fckeditor/editor/images/smiley/msn/teeth_smile.gif
Nothing will be replaced
Hope it helps, and that i didn't miss anything.
UPDATE
Obviously considering you are using an HTML parser to find the URL inside the a href (which you should in case you're not :-))
Cheers
That is for perl, I do not know c#:
s#(<(img|a)\s[^>]*?\s(src|href)=)(["'])http://[^'"]*?(/Content/[^'"]*?)\4#$1$4$5#g
If c# has perl-like regex it will be easy to port.
This function can convert all the hyperlinks and image sources inside your HTML to absolute URLs and for sure you can modify it also for CSS files and Javascript files easily:
Private Function ConvertALLrelativeLinksToAbsoluteUri(ByVal html As String, ByVal PageURL As String)
Dim result As String = Nothing
' Getting all Href
Dim opt As New RegexOptions
Dim XpHref As New Regex("(href="".*?"")", RegexOptions.IgnoreCase)
Dim i As Integer
Dim NewSTR As String = html
For i = 0 To XpHref.Matches(html).Count - 1
Application.DoEvents()
Dim Oldurl As String = Nothing
Dim OldHREF As String = Nothing
Dim MainURL As New Uri(PageURL)
OldHREF = XpHref.Matches(html).Item(i).Value
Oldurl = OldHREF.Replace("href=", "").Replace("HREF=", "").Replace("""", "")
Dim NEWURL As New Uri(MainURL, Oldurl)
Dim NewHREF As String = "href=""" & NEWURL.AbsoluteUri & """"
NewSTR = NewSTR.Replace(OldHREF, NewHREF)
Next
html = NewSTR
Dim XpSRC As New Regex("(src="".*?"")", RegexOptions.IgnoreCase)
For i = 0 To XpSRC.Matches(html).Count - 1
Application.DoEvents()
Dim Oldurl As String = Nothing
Dim OldHREF As String = Nothing
Dim MainURL As New Uri(PageURL)
OldHREF = XpSRC.Matches(html).Item(i).Value
Oldurl = OldHREF.Replace("src=", "").Replace("src=", "").Replace("""", "")
Dim NEWURL As New Uri(MainURL, Oldurl)
Dim NewHREF As String = "src=""" & NEWURL.AbsoluteUri & """"
NewSTR = NewSTR.Replace(OldHREF, NewHREF)
Next
Return NewSTR
End Function