I have a method that serializes an object to a string, exhibit a:
Shared Function Serialize(ByVal o As Object) As String
Dim rtnVal As String = ""
Dim x As New System.Xml.Serialization.XmlSerializer(o.GetType())
Using memStream As New MemoryStream
Dim stWriter As New System.IO.StreamWriter(memStream)
x.Serialize(stWriter, o)
rtnVal = Encoding.UTF8.GetString(memStream.GetBuffer())
End Using
Return rtnVal
End Function
Using this serialized data, I'm now inserting it into an XML typed field in my SQL 2012 database. Most of the time, this code works very well, but for a particular object, I'm getting "invalid" characters, namely the error "parsing line 5 character 17 illegal xml character". I took a look at my data, and it's clean, as you can see here:
<?xml version="1.0" encoding="utf-8"?>
<RatingDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<LenderName>dsfg</LenderName>
<VehiclePrice>345</VehiclePrice>
</RatingDetails>
Some snooping led me do the IsXMLChar method - http://msdn.microsoft.com/en-us/library/system.xml.xmlconvert.isxmlchar%28v=vs.100%29.aspx - and using this I was able to loop through each character in my serialized XML string. Low and behold, I DO have invalid data. I have 15 "" character's at the end of my string - WTF!?!
So my questions to you all are, where the heck are the extra "'s coming from, why cant I see them when I inspect the string in my quick watch, and how do I prevent em in the first place.
Yours in ASP.NET, ewitkows
The problem is you are calling MemoryStream.GetBuffer. According to the MSDN article:
Note that the buffer contains allocated bytes which might be unused. For example, if the string "test" is written into the MemoryStream object, the length of the buffer returned from GetBuffer is 256, not 4, with 252 bytes unused. To obtain only the data in the buffer, use the ToArray method; however, ToArray creates a copy of the data in memory.
To fix it, you could call ToArray instead:
Shared Function Serialize(ByVal o As Object) As String
Dim rtnVal As String = ""
Dim x As New System.Xml.Serialization.XmlSerializer(o.GetType())
Using memStream As New MemoryStream
Dim stWriter As New System.IO.StreamWriter(memStream)
x.Serialize(stWriter, o)
rtnVal = Encoding.UTF8.GetString(memStream.ToArray())
End Using
Return rtnVal
End Function
However, that's still not really efficient. If the stream contains a lot of data, it's going to copy the whole thing into a new array for no reason. For peace of mind, I would recommend using the StreamReader to read the MemoryStream rather than trying to decode it yourself (but don't forget to seek back to the beginning of the stream before reading it):
Public Function Serialize(ByVal o As Object) As String
Dim rtnVal As String = ""
Dim x As New System.Xml.Serialization.XmlSerializer(o.GetType())
Using memStream As New MemoryStream
Dim stWriter As New System.IO.StreamWriter(memStream)
x.Serialize(stWriter, o)
Dim reader As New StreamReader(memStream)
memStream.Position = 0 ' Seek to start of stream
rtnVal = reader.ReadToEnd()
End Using
Return rtnVal
End Function
Related
I have a handler that handles single files (text based) perfectly. I can receive .zip files but they are unable to be accessed due to "corruption" errors. I know that this is due to reading things in as a text stream and not a byte array but I cannot figure it out. (My attempt is below)
EDIT:
I need to be able to have the handler accept .zips without corruption errors. I got past the corruption errors but the below code handles the file without corruption issues but unzips it with no files inside.
Sub ProcessRequest(ByVal context as HttpContent) Implements IHTTPHandler.ProcessRequest
Try
If Context.Request.HttpMethod() = "POST" Then
context.Response.ContentType = "application/octet-stream"
context.Response.StatusCode = 204
Dim reader as New System.IO.BinaryReader(context.Request.InputStream)
Dim contents as Byte
Dim int as Integer = reader.Basestream.Length
''Problem has got to be here, This loop structure can't be right..
Do While int > 0
contents = reader.readByte()
System.IO.File.WriteAllText("thisismyoutputdirectory"), filename), contents)
Loop
else
''Handle non post cases
end if
Catch ex as Exception
''Error Handling is here
End Try
End Sub
Instead of Streamreader I am using BinaryReader. I have attempted to save contents as a byte array and then write them all out using the WriteAllBytes method.
I will continue experiementing but any guidance would be great!
I just solved the issue. I simply needed to write it out to a byte array and save an integer to represent the number of bytes. Then simply print the contents. It looks like I was trying to make things more complicated.
My loop in original code is kinda ugly :(
Sub ProcessRequest(ByVal context as HttpContext) Implements IHttpHandler.ProcessRequest
Try
''If the handler receives a POST requent then perform these actions
If context.Request.HttpMethod() = "POST" Then
context.Response.ContentType = "application/octet-Stream"
context.Response.StatusCode = 204
''Get the filename out of the requests header
Dim filename as String = context.Request.Header("filename")
''Get the numbytes for the .zip and save them as a byte array
Dim numbytes as Integer = reader.BaseStream.Length
Dim contents() as Byte = reader.ReadBytes(numbytes)
''Write the byte array out to the file
System.IO.File.WriteAllBytes("This/is/my/path/" & filename, contents)
else
'' Handle has no work to do since request was not a POST
End if
Catch
''Error Handling is here
End Try
I'm trying to resize one image to 5 different sizes (and then upload them to amazonS3).
I'm using imageresizer.net
the problem seems to be that i cannot use the inputstream twice. it works the first time.
Dim SmallStream As Stream = New MemoryStream
Dim TinyStream As Stream = New MemoryStream
If FileUpload1.HasFile Then
**ImageResizer.ImageBuilder.Current.Build(FileUpload1.PostedFile.InputStream, SmallStream, New ResizeSettings("maxwidth=100&maxheight=100"))
ImageResizer.ImageBuilder.Current.Build(FileUpload1.PostedFile.InputStream, TinyStream, New ResizeSettings("maxwidth=100&maxheight=100"))**
AmazonUploadFile("SmallImages/" & FileUpload1.FileName, SmallStream)
AmazonUploadFile("TinyImages/" & FileUpload1.FileName, TinyStream)
End If
Public Shared Function GetS3Client() As AmazonS3
Dim appConfig As NameValueCollection = ConfigurationManager.AppSettings
Dim s3Client As AmazonS3 = AWSClientFactory.CreateAmazonS3Client(AWS_ACCESS_KEY, AWS_SECRET_KEY)
Return s3Client
End Function
Public Sub AmazonUploadFile(S3Key As String, FileStream As Stream)
Dim request As New PutObjectRequest()
request.WithBucketName(BUCKET_NAME)
request.WithKey(S3Key).InputStream = FileStream
request.WithCannedACL(S3CannedACL.PublicRead)
GetS3Client.PutObject(request)
End Sub
The code breaks when i try to retrieve the FileUpload1.PostedFile.InputStream the second time.
I believe ImageResizer will accept Image objects to that method. So, you could read the InputStream into an Image object first (Image.FromStream()), then you can use that image object repeatedly.
[Disclaimer: I'm the author of http://imageresizing.net/]
Pass FileUpload1.PostedFile instead of FileUpload1.PostedFile.InputStream to the ImageResizer and it will automatically handle re-seeking the stream to the beginning after each read. (Make sure you're using 3.1.5 or later).
Alternatively, use the ImageJob class and set ResetSourceStream=true.
I use the following two methods to encrypt and decrypt strings:
'Encrypts string. Returns encrypted byte array.
Public Function Encrypt(ByVal str As String) As Byte()
Dim inputInBytes() As Byte = Encoding.Unicode.GetBytes(str)
Dim laesProvider As New AesCryptoServiceProvider()
laesProvider.Key = _key
laesProvider.Mode = CipherMode.CBC
laesProvider.IV = _IV
laesProvider.Padding = PaddingMode.PKCS7
Dim lencryptor As ICryptoTransform = laesProvider.CreateEncryptor
Dim encryptedStream As New MemoryStream
Dim cryptStream As CryptoStream = New CryptoStream(encryptedStream, lencryptor, CryptoStreamMode.Write)
cryptStream.Write(inputInBytes, 0, inputInBytes.Length)
cryptStream.FlushFinalBlock()
encryptedStream.Position = 0
Dim result(encryptedStream.Length - 1) As Byte
encryptedStream.Read(result, 0, encryptedStream.Length)
cryptStream.Close()
Return result
End Function
'Decrypts bytearray. Returns string.
Public Function DecryptToStr(ByVal inputInBytes() As Byte) As String
Dim laesProvider As New AesCryptoServiceProvider()
laesProvider.Key = _key
laesProvider.Mode = CipherMode.CBC
laesProvider.IV = _IV
laesProvider.Padding = PaddingMode.PKCS7
Dim ldecryptor As ICryptoTransform = laesProvider.CreateDecryptor
' Provide a memory stream to decrypt information into
Dim decryptedStream As MemoryStream = New MemoryStream()
Dim cryptStream As CryptoStream = New CryptoStream(decryptedStream, ldecryptor, CryptoStreamMode.Write)
cryptStream.Write(inputInBytes, 0, inputInBytes.Length)
cryptStream.FlushFinalBlock() '#### This is where the exception is thrown ####
decryptedStream.Position = 0
' Read the memory stream and convert it back into a string
Dim result(decryptedStream.Length - 1) As Byte
decryptedStream.Read(result, 0, decryptedStream.Length)
cryptStream.Close()
Return Encoding.Unicode.GetString(result)
End Function
The error occurs when attempting to decrypt certain length strings. When the string is a social security # (11 chars including dashes) then is throws "The input data is not a complete block" CryptographicException. If I pass in for example a string that is exactly 8 characters long, then everything works as expected. I thought that the PKCS7 padding would take care of the various lengths. I'm sure that I'm missing something simple, but after hours of googling, the answer eludes me.
The issue wasn't in the encryption method, it was in the length of the varbinary set in the database where it was being stored. So the encrypted data was being truncated.
I can't seem to get past this problem, I've created what I assume to be a 256-bit key using the random generator at GRC and combined that with my IV. I keep getting the error below:
Specified key is not a valid size for this algorithm.
Any help is gratefully received, here is the code I am using to Encrypt/Decrypt:
Private Function Rijndael(ByVal sInput As String, ByVal bEncrypt As Boolean) As String
' Setup the Key and Initialization Vector.
Dim byteKey As Byte() = Encoding.UTF8.GetBytes("C3CA193570B26E5C3CBB50FD805A0E23BAFFABA135E82C41517EEDCB9B7C90AC")
Dim byteIV As Byte() = Encoding.UTF8.GetBytes("c+O2r)J~?L:$]u[2")
' Create an instance of the encyrption algorithm.
Dim _rijndael As New RijndaelManaged()
' Create an encryptor using our key and IV
Dim transform As ICryptoTransform
If bEncrypt Then
transform = _rijndael.CreateEncryptor(byteKey, byteIV)
Else
transform = _rijndael.CreateDecryptor(byteKey, byteIV)
End If
' Create the streams for input and output
Dim msOutput As New System.IO.MemoryStream()
Dim msInput As New CryptoStream(msOutput, transform, CryptoStreamMode.Write)
' Feed data into the crypto stream.
msInput.Write(Encoding.UTF8.GetBytes(sInput), 0, Encoding.UTF8.GetBytes(sInput).Length)
' Flush crypto stream.
msInput.FlushFinalBlock()
Return Convert.ToBase64String(msOutput.ToArray)
End Function
C3CA193570B26E5C3CBB50FD805A0E23BAFFABA135E82C41517EEDCB9B7C90AC is hex code for a 256-bit-long bitstream.
But Encoding.UTF8.GetBytes does not turn hex code into the corresponding bytes in the way you were thinking.
Because you had 64 characters in that string, you get 64 bytes of UTF-8 bytes. That's a 512-bit-long bitstream.
I have a function that dynamically adds text to an image in a predesignated spot. Originally I did it with jpegs, and it was working. I switched to PNG so the images would be better quality, as the original jpegs were kind of pixely. Anyway, here is my code. It executes down to the oBitmap.Save(), then dies with "A General Error Has Occurred in GDI+".
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
context.Response.ContentType = "image/png"
context.Response.Clear()
context.Response.BufferOutput = True
Try
Dim oText As String = context.Server.HtmlDecode(context.Request.QueryString("t"))
If String.IsNullOrEmpty(oText) Then oText = "Placeholder"
Dim oPType As String = context.Server.HtmlDecode(context.Request.QueryString("p"))
If String.IsNullOrEmpty(oPType) Then oPType = "none"
Dim imgPath As String = ""
Select Case oPType
Case "c"
imgPath = "img/banner_green.png"
Case "m"
imgPath = "img/banner_blue.png"
Case Else
Throw New Exception("no ptype")
End Select
Dim oBitmap As Bitmap = New Bitmap(context.Server.MapPath(imgPath))
Dim oGraphic As Graphics = Graphics.FromImage(oBitmap)
Dim frontColorBrush As New SolidBrush(Color.White)
Dim oFont As New Font(FONT_NAME, 30)
Dim oInfo() As ImageCodecInfo = ImageCodecInfo.GetImageEncoders
Dim oEncoderParams As New EncoderParameters(2)
Dim xOffset As Single = Math.Round((oBitmap.Height - oFont.Height) / 2, MidpointRounding.ToEven)
Dim oPoint As New PointF(275.0F, xOffset + 10)
oEncoderParams.Param(0) = New EncoderParameter(Encoder.Quality, 100L)
oEncoderParams.Param(1) = New EncoderParameter(Encoder.ColorDepth,8L)
oGraphic.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
oGraphic.DrawString(oText, oFont, frontColorBrush, oPoint)
oBitmap.Save(context.Response.OutputStream, oInfo(4), oEncoderParams)
context.Response.Output.Write(oBitmap)
oFont.Dispose()
oGraphic.Dispose()
oBitmap.Dispose()
context.Response.Flush()
Catch ex As Exception
End Try
End Sub
The only changes I made to this from the jpeg version are:
context.Response.ContentType = "image/jpeg" changed to "image/png"
changed base images (img/banner_green.jpg, img/banner_blue.jpg) to .png
added the second encoding parameter specifying color depth
changed oInfo(1) (jpeg) to oInfo(4) (png)
Are there more things I need to tweak to get this routine to properly generate the PNG?
According to this post, Bitmap.Save requires a seekable stream to save as PNG, which HttpResponse.OutputStream isn't. You'll have to save the image into a MemoryStream first, and then copy the contents of it to Response.OutputStream, like:
Dim tempStream as New MemoryStream
oBitmap.Save(tempStream, ImageFormat.Png, oEncoderParams)
Response.OutputStream.Write(tempStream.ToArray(), 0, tempStream.Length)
Also note that the line
context.Response.Output.Write(oBitmap)
does something different then what you are probably expecting. HttpResponse.Output is a TextWriter, and the overload you use here, TextWriter.Write(object) will just call ToString on the object and write the results into the stream, what in this case results in writing "System.Drawing.Bitmap" to the output.
You're disposing of the bitmap before you're flushing the Response? Try flipping that around. Also, it looks like you're writing the bitmap to the stream twice. I'm not sure why you're doing that. Save the bitmap to the output stream or use the Write method of the Response object, but not both.