Saving POST binary data with ADODB.Stream - asp-classic

Another website sends me data with the POST method.
I would like to take this data and insert it into the database.
After some research online, I concluded that ADODB.Stream should do the job for me.
I dont have problem with getting the binary data with Request.TotalBytes.
With the following code, I am not receiving an error but it does not save the data either. So I must be doing something wrong with the ADODB Stream.
tot_bytes = Request.TotalBytes
Set BinaryStream = CreateObject("ADODB.Stream")
BinaryStream.Mode = 3
BinaryStream.Type = 1
BinaryStream.Open
gBinaryData = BinaryStream.Write(tot_bytes)
BinaryStream.Close
Set BinaryStream = Nothing
SQL = "INSERT INTO STATUSES (StatusMessage, StatusDateEntered) VALUES ('"& gBinaryData &"', '"& FormatDateMySQL(NOW) &"')"
Set objAddC = objConn.execute(SQL)
.
Following a successful subscription, Facebook will proceed to call your endpoint every time that there are changes (to the chosen fields or connections). For each update, it will make an HTTP POST request.
The request will have content type of application/json and the body will comprise a JSON-encoded string containing one or more changes.
Note for PHP developers: In PHP, to get the encoded data you would use the following code:
$data = file_get_contents("php://input");
$json = json_decode($data);

First of all, Write method does not return anything, in fact it's just a sub-routine. And Request.TotalBytes is just length of request in bytes. When you need to read request data as array of bytes, you should use Request.BinaryRead(length of bytes). Therefore, in Stream object, you need to read all of bytes using Read method after the writing bytes and setting position to the start.
However, it seems not neccessary in this case if you have to store the data as binary.
I assume that you need the data as text, most likely it's json string. So, you should convert data to string from bytes.
Note that, you need to handle an exception about total bytes. If the Request does not contain anything, Request.TotalBytes equals to zero and since Request.BinaryRead expects a number bigger than zero and less than or equal to total bytes an error occurs.
Dim tot_bytes, postData
tot_bytes = Request.TotalBytes
If tot_bytes > 0 Then
With Server.CreateObject("Adodb.Stream")
.Charset = "utf-8" 'specify the request encoding
.Type = 1 'adTypeBinary, a binary stream
.Open
.Write Request.BinaryRead(tot_bytes) 'Write bytes
.Position = 0 ' set position to the start
.Type = 2 ' adTypeText, stream type is text now
postData = .ReadText 'read all text
.Close
End With
With Server.CreateObject("Adodb.Recordset")
.Open "STATUSES", objConn , , 3
.AddNew
.Fields("StatusMessage").Value = postData
.Fields("StatusDateEntered").Value = Now()
.Update
.Close
End With
Response.Write "data stored successfully"
Else
Response.Write "no post data"
End If

Beside the fact that it is a very bad idea to directly insert your data into a database like this (possible sql injection), how do you post for form data? CLassic ASP can not handle binary data directly either. So this won't work at all.
So whatever you post, first you have to make sure that you post with form enctype="multiform/data".
To get the data into a object try this instead:
byteArray = Request.BinaryRead(Request.TotalBytes)
BUt to handle it, store to database, or save to a file, you need a component, e.g. http://www.motobit.com/help/scptutl/upload.asp or try check this article (special when you upload more thatn just one file): http://www.codeguru.com/csharp/.net/net_asp/article.php/c19297/Pure-ASP-File-Upload.htm.
EDIT:
Antonin Fuller has also made a sample ASP code without using his DLL.
Try this, too:
Private Function RSBinaryToString(xBinary)
'Antonin Foller, http://www.motobit.com
'RSBinaryToString converts binary data (VT_UI1 | VT_ARRAY Or MultiByte string)
'to a string (BSTR) using ADO recordset
Dim Binary
'MultiByte data must be converted To VT_UI1 | VT_ARRAY first.
If vartype(xBinary)=8 Then Binary = MultiByteToBinary(xBinary) Else Binary = xBinary
Dim RS, LBinary
Const adLongVarChar = 201
Set RS = CreateObject("ADODB.Recordset")
LBinary = LenB(Binary)
If LBinary>0 Then
RS.Fields.Append "mBinary", adLongVarChar, LBinary
RS.Open
RS.AddNew
RS("mBinary").AppendChunk Binary
RS.Update
RSBinaryToString = RS("mBinary")
Else
RSBinaryToString = ""
End If
End Function
See more here: http://www.motobit.com/tips/detpg_binarytostring/
Finally you should get the stream, convert it, and work with it.

Related

How to get the request.body value in asp classic?

In an .asp classic page I´m getting a POST send to me(a JSON string) and it is send in the request.body, says the guy how send it.
But if I just have theresponse=request.form I am not getting anything?
So how do I get the value from a request.body?
Some payment gateway API's I've used in the past have sent responses in this fashion. The data (JSON) is sent as a binary body post.
To read it you need to use Request.BinaryRead with Request.TotalBytes, then use Adodb.Stream to convert the binary to UTF8 text:
Response.ContentType = "application/json"
Function BytesToStr(bytes)
Dim Stream
Set Stream = Server.CreateObject("Adodb.Stream")
Stream.Type = 1 'adTypeBinary
Stream.Open
Stream.Write bytes
Stream.Position = 0
Stream.Type = 2 'adTypeText
Stream.Charset = "utf-8"
BytesToStr = Stream.ReadText
Stream.Close
Set Stream = Nothing
End Function
' You shouldn't really be receiving any posts more than a few KB,
' but it might be wise to include a limit (200KB in this example),
' Anything larger than that is a bit suspicious. If you're dealing
' with a payment gateway the usual protocol is to post the JSON
' back to them for verification before processing.
if Request.TotalBytes > 0 AND Request.TotalBytes <= 200000 then
Dim postBody
postBody = BytesToStr(Request.BinaryRead(Request.TotalBytes))
Response.Write(postBody) ' the JSON... hopefully
end if

Correct way of streaming a CSV of unknown size from memory

I have a database that has a table with just over 2 million records of about 20 columns. The user is able to query the database and limit the number of records returned so the recordset may be from 1 to 2 million.
As it is tabular information I want to send the data as a CSV. I'm using a StreamWriter to write the data to memory and once the file is complete I'm sending it as an HttpResponseMessage. My code is below, and it works fine as long as I don't run out of memory. Is there a way for me to stream the file as it's being processed so that the memory used is minimal?
<HttpGet, Route("api/records/export")>
Public Function ExportRecords() As HttpResponseMessage
Dim stream As New MemoryStream
Dim writer As New StreamWriter(stream)
writer.WriteLine("")
' Processing of data here
writer.WriteLine("""Write Data to MemoryStream"")
writer.Flush()
stream.Position = 0
Dim result As New HttpResponseMessage(HttpStatusCode.OK)
result.Content = New StreamContent(stream)
result.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("text/csv")
result.Content.Headers.ContentDisposition = New Headers.ContentDispositionHeaderValue("attachment") _
With {.FileName = "I" & Format(Date.Now, "yyMMdd") & ".csv"}
Return result
End Function
I've read on StackOverflow answers to questions such as Returning binary file from controller in ASP.NET Web API but these all deal with streaming a web response from a file stored on disk and not from memory.
As the comment on my question suggested I used PushStreamContent to stream the contents of my database to the browser in CSV format. I also made it asynchronous to get even more performance out of the export.
There is an important limitation with using PushStreamContent to stream content to the client from the server. Since the 200 OK header is sent first before anything is streamed, the response doesn't know ahead of time if there's going to be an error in the results as they're returned. If something goes wrong while the results are being sent, the client will just see a generic network error on its end. It's up to the server to log any error so that you can check server logs to find the specific error.
Here is the code for PushStreamContent that I used (the error checking is removed for brevity).
Dim result As HttpResponseMessage = New HttpResponseMessage(HttpStatusCode.OK) With {
.Content = New PushStreamContent(Async Function(outStream, httpContent, context)
Dim writer As StreamWriter = New StreamWriter(outStream)
Await writer.WriteLineAsync("""Header 1"",""Header 2"",""Header 3""")
For Each item In returnItems
Await writer.WriteLineAsync("""" & item.Col1.ToString & """,=""" & item.Col2.ToString & """,=""" & item.Col3.ToString & """")
Await writer.FlushAsync()
Next
outputStream.Close()
End Function)}
result.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("text/csv")
result.Content.Headers.ContentDisposition = New _
Headers.ContentDispositionHeaderValue("attachment") With {.FileName = "MyCSV.csv"}
Return result

Visual basic and Json.net Web request

Basically what im trying to do is make a program that list game information for league of legends.. using there API to extract data. how this works is you Search there username and it returns an integer linked to that account, you then use that integer to search for all of the information for that account, EG account level, wins, losses, etc.
I've run into a problem i can't seem to figure out.. Please not that I'm very new to Json.net so have little experience about working with it.. Below is how the search for the user ID is found, The First section is the Username Minus Any spaces in the name the next is the ID which is the information i require.
{"chucknoland":{"id":273746,"name":"Chuck Noland","profileIconId":662,"summonerLevel":30,"revisionDate":1434821021000}}
I must be declaring the variables wrong in order to obtain the data as everything i do it returns as 0.
these are the following class i have to store the ID within
Public Class ID
Public Shared id As Integer
Public Shared name As String
End Class
Looking at a previous example seen here Simple working Example of json.net in VB.net
They where able to resolve there issue by making a container class with everything inside it.. My problem is that The data i seek i always changing.. The first set will always be different to the "Chucknoland" that's displayed in the example.. is someone able to explain how i could go about extracting this information?
Please note that the variables rRegion has the value of what server there on, Chuck Noland is on OCE, and sSearch is the Username.. Due to Problems with API keys i had to remove the API key from the code... But the URL returns the Json Provided.
'URL string used to grab Summoner ID
jUrlData = "https://oce.api.pvp.net/api/lol/" + rRegion + "/v1.4/summoner/by-name/" + sSearch +
' Create a request for URL Data.
Dim jsonRequest As WebRequest = WebRequest.Create(jUrlData)
'request a response from the webpage
Dim jsonResponse As HttpWebResponse = CType(jsonRequest.GetResponse(), HttpWebResponse)
'Get Data from requested URL
Dim jsonStream As Stream = jsonResponse.GetResponseStream()
'Read Steam for easy access
Dim jsonReader As New StreamReader(jsonStream)
'Read Content
Dim jsonResponseURL As String = jsonReader.ReadToEnd()
jUrlString = jsonResponseURL
this is the request i have to obtain the information, and this is the code i tried to use to display the ID for that json.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim obj As ID
obj = JsonConvert.DeserializeObject(Of ID)(jUrlString)
MsgBox(obj.id)
End Sub
Is anyone able to explain how i can go about getting this to work?
One way to handle this would be to get the item into a Dictionary where the keys are the property names.
The class you have is not quite right unless you only want name and id and not the rest of the information. But using a Dictionary you wont need it anyway. The "trick" is to skip over the first part since you do not know the name. I can think of 2 ways to do this, but there are probably more/better ways.
Since json uses string keys pretty heavily, create a Dictionary, then get the data from it for the actual item:
jstr = ... from whereever
' create a dictionary
Dim jResp = JsonConvert.DeserializeObject(Of Dictionary(Of String, Object))(jstr)
' get the first/only value item
Dim jobj = jResp.Values(0) ' only 1 item
' if you end up needing the name/key:
'Dim key As String = jResp.Keys(0)
' deserialize first item to dictionary
Dim myItem = JsonConvert.DeserializeObject(Of Dictionary(Of String, Object))(jobj.ToString)
' view results
For Each kvp As KeyValuePair(Of String, Object) In myItem
Console.WriteLine("k: {0} v: {1}", kvp.Key, kvp.Value.ToString)
Next
Output:
k: id v: 273746
k: name v: Chuck Noland
k: profileIconId v: 662
k: summonerLevel v: 30
k: revisionDate v: 1434821021000
Using String, String may also work, but it would convert numerics to string (30 becomes "30") which is usually undesirable.
While poking around I found another way to get at the object data, but I am not sure if it is a good idea or not:
' parse to JObject
Dim js As JObject = JObject.Parse(jstr)
' 1 = first item; 2+ will be individual props
Dim jT As JToken = js.Descendants(1)
' parse the token to String/Object pairs
Dim myItem = JsonConvert.DeserializeObject(Of Dictionary(Of String, Object))(jT.ToString)
Same results.

I have path Manipulation issue. The following code is placed in Page_load method of ASPx page + VB

If Request.QueryString("ID") = "" Then
folderDirectory = Global.FileUpload.GetFolderDirectory(Request.QueryString("TFID"))
If Not File.Exists(folderDirectory + fileName) Then
If Not Directory.Exists(folderDirectory) Then
Directory.CreateDirectory(folderDirectory)
End If
Dim bufferSize As Integer = Me.fileUpload.PostedFile.ContentLength
Dim buffer As Byte() = New Byte(bufferSize) {}
' write the byte to disk
Using fs As New FileStream(Path.Combine(folderDirectory, fileName), FileMode.Create)
Dim bytes As Integer = Me.fileUpload.PostedFile.InputStream.Read(buffer, 0, bufferSize)
' write the bytes to the file stream
fs.Write(buffer, 0, bytes)
End Using
Else
CallOnComplete("error", "", "Error uploading '" & fileName & "'. File has been exists!")
Exit Sub
End If
But Fortify scan report for the above sample code shows Path Manipulation issue as high. I Need help to modify above code so that it can pass fortify scan
It is showing me error at folderDirectory
Usually, when your code works inside a web application you don't have the liberty to use the full file system as you do on your local PC. Any kind of 'Path Manipulation' is suspicious.
You should try to recode your works using Server.MapPath method.
Pay particular attention to this warning
For security reasons, the AspEnableParentPaths property has a default value set to FALSE.
Scripts will not have access to the physical directory structure unless AspEnableParentPaths
is set to TRUE.

ASP SaveToDisk method takes an incredible amount of time

This is a method in ASP Classic that saves a file to disk. It takes a very long time but I'm not sure why. Normally, I wouldn't mind so much, but the files it handles are pretty large so need this needs to faster than 100kB a second save. Seriously slow. (old legacy system, band aid fix till it gets replaced...)
Public Sub SaveToDisk(sPath)
Dim oFS, oFile
Dim nIndex
If sPath = "" Or FileName = "" Then Exit Sub
If Mid(sPath, Len(sPath)) <> "\" Then sPath = sPath & "\" '"
Set oFS = Server.CreateObject("Scripting.FileSystemObject")
If Not oFS.FolderExists(sPath) Then Exit Sub
Set oFile = oFS.CreateTextFile(sPath & FileName, True)
For nIndex = 1 to LenB(FileData)
oFile.Write Chr(AscB(MidB(FileData,nIndex,1)))
Next
oFile.Close
End Sub
I'm asking because there are plenty of WTF's in this code so I'm fighting those fires while getting some help on these ones.
I don't see your definition for "FileData" anywhere in your code - where is this coming from? Is there a reason you're writing it to disk a single character at a time? I'd suspect this is your problem - writing 100K of data takes 100K trips through this loop, which could be the reason for your slowdown. Why can't you replace the write loop at the bottom:
For nIndex = 1 to LenB(FileData)
oFile.Write Chr(AscB(MidB(FileData,nIndex,1)))
Next
with a single statement to write the file all at once?
oFile.Write FileData
What you should do is read the binary request into an ADODB.Stream object and convert it to plain ASCII text in a single fast step.
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Type = 1
objStream.Open
objStream.Write Request.BinaryRead(Request.TotalBytes)
objStream.Position = 0
objStream.Type = 2
objStream.Charset = "ISO-8859-1"
FormData = objStream.ReadText
objStream.Close
Set objStream = Nothing
Notice how the variable FormData now contains the form data as text. Then you parse this text and locate the start and length of each file, and use ADODB.Stream CopyTo method to extract the specific portion of the file and save it do disk.

Resources