Integrating Amazon Polly with VBA - vba

I want to use Amazon Polly in an PowerPoint VBA Project to convert notes to mp3.
I think there is no SDK for this, but it is possible through the API with the signing?

See VBA Module and VBA Class below which I recently constructed, permitting direct calls to the Amazon web service Polly from within MS Office, saving the resulting MP3 to a file. A couple of notes:
I have only confirmed successful calls to Amazon Polly with Office 365 versions of MS Word and MS Excel on Windows 10, but given my past VBA experience, don't have any reason to believe the code won't work on other variations.
The entry point is callAwsPolly.
You will need to adjust the AWS parameters accordingly (ie, aws.host, aws.region, aws.service, & aws.uri).
The credentials (aws.accessKey & aws.secretKey) are the standard AWS example credentials, and will obviously need to be replaced with your own AWS credentials.
Note that it is very bad practice to embed credentials in code. Am not sure of your specific application, but in my case, I created my own key store, using optionally a passphrase or the MAC address as the decryption key for the key store. The concept of credential management is another subject, which I am not addressing here...
To incorporate the functionality into an MS Office product, add the following as a VBA module...
Option Explicit
Sub callAwsPolly()
Dim aws As amazonWebService
Set aws = New amazonWebService
aws.host = "polly.us-east-2.amazonaws.com"
aws.region = Split(aws.host, ".")(1)
aws.service = "polly"
aws.uri = "/v1/speech"
' !!! NOTE: The following is very bad practice to embed credentials in code!!!
aws.accessKey = "AKIAIOSFODNN7EXAMPLE"
aws.secretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
Dim requestParameters As String
requestParameters = "{#OutputFormat#: #mp3#, #Text#: #Polly want a cracker?#, #TextType#: #ssml#, #VoiceId#: #Emma#}"
requestParameters = Replace(requestParameters, "#", """")
Dim httpResponse As Object
Set httpResponse = aws.callWebService("application/json", requestParameters)
If httpResponse Is Nothing Then
MsgBox "Call to AWS Polly failed."
ElseIf httpResponse.Status = 200 Then
MsgBox "Call to AWS Polly succeeded! MP3 file being saved."
Dim iFile As Long: iFile = FreeFile
Open ActiveWorkbook.FullName & ".mp3" For Binary Access Write As #iFile
Put iFile, , httpResponse.Responsebody
Close #iFile
Else
MsgBox "Call to AWS Polly failed:" + CStr(httpResponse.Status) + " " + httpResponse.StatusText + " " + httpResponse.ResponseText
End If
End Sub
' Adapted from https://stackoverflow.com/questions/36384741/cant-use-getbytes-and-computehash-methods-on-vba
Public Function hmacSha256(key As Variant, stringToHash As Variant) As Byte()
Dim ssc As Object
Set ssc = CreateObject("System.Security.Cryptography.HMACSHA256")
ssc.key = str2byte(key)
hmacSha256 = ssc.ComputeHash_2(str2byte(stringToHash))
Set ssc = Nothing
End Function
Public Function sha256(stringToHash As Variant) As Byte()
Dim ssc As Object
Set ssc = CreateObject("System.Security.Cryptography.SHA256Managed")
sha256 = ssc.ComputeHash_2(str2byte(stringToHash))
Set ssc = Nothing
End Function
Public Function str2byte(s As Variant) As Byte()
If VarType(s) = vbArray + vbByte Then
str2byte = s
ElseIf VarType(s) = vbString Then
str2byte = StrConv(s, vbFromUnicode)
Else
Exit Function
End If
End Function
Public Function byte2hex(byteArray() As Byte) As String
Dim i As Long
For i = 0 To UBound(byteArray)
byte2hex = byte2hex & Right(Hex(256 Or byteArray(i)), 2)
Next
byte2hex = LCase(byte2hex)
End Function
...and add the following code as a VBA Class Module, naming it "amazonWebService"...
'
' Class Module: amazonWebService
'
Private className As String
Public host As String
Public region As String
Public service As String
Public uri As String
Public accessKey As String
Public secretKey As String
Private Sub Class_Initialize()
className = "amazonWebService"
host = ""
region = ""
uri = ""
service = ""
accessKey = ""
secretKey = ""
End Sub
Public Function callWebService(contentType As String, requestParameters As String) As Object
If host = "" Or region = "" Or uri = "" Or service = "" Or accessKey = "" Or secretKey = "" Then
Err.Raise _
1000, _
className, _
className + ": Please set properties before calling the amazon web service: host, region, uri, service, accessKey, and secretKey."
callWebService = Null
Exit Function
End If
Dim amzDate As String, dateStamp As String
amzDate = date2iso8601(GMT())
dateStamp = Left(amzDate, 8)
' ************* TASK 1: CREATE A CANONICAL REQUEST *************
' http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
'Step 1 is to define the verb (GET, POST, etc.)
Dim method As String
method = "POST"
' Step 2: Create canonical URI (set by the calling program)
Dim canonicalUri As String
canonicalUri = uri
' Step 3: Create the canonical query string. In this example (POST), request
' parameters are passed in the body of the request and the query string
' is blank.
Dim canonicalQueryString As String
canonicalQueryString = ""
' Step 4: Create the canonical headers. Header names must be trimmed
' and lowercase, and sorted in code point order from low to high.
' Note that there is a trailing \n.
Dim canonicalHeaders As String
canonicalHeaders = _
"host:" + host + vbLf + _
"x-amz-date:" + amzDate + vbLf
' Step 5: Create the list of signed headers. This lists the headers
' in the canonical_headers list, delimited with ";" and in alpha order.
' Note: The request can include any headers; canonical_headers and
' signed_headers include those that you want to be included in the
' hash of the request. "Host" and "x-amz-date" are always required.
Dim signedHeaders As String
signedHeaders = "host;x-amz-date"
' Step 6: Create payload hash. In this example, the payload (body of
' the request) contains the request parameters.
Dim payloadHash As String
payloadHash = byte2hex(sha256(requestParameters))
' Step 7: Combine elements to create canonical request
Dim canonicalRequest As String
canonicalRequest = method + vbLf + _
canonicalUri + vbLf + _
canonicalQueryString + vbLf + _
canonicalHeaders + vbLf + _
signedHeaders + vbLf + _
payloadHash
' ************* TASK 2: CREATE THE STRING TO SIGN*************
' Match the algorithm to the hashing algorithm you use, either SHA-1 or
' SHA-256 (recommended)
Dim algorithm As String, credentialScope As String, stringToSign As String
algorithm = "AWS4-HMAC-SHA256"
credentialScope = dateStamp + "/" + region + "/" + service + "/aws4_request"
stringToSign = _
algorithm + vbLf + _
amzDate + vbLf + _
credentialScope + vbLf + _
byte2hex(sha256(canonicalRequest))
' ************* TASK 3: CALCULATE THE SIGNATURE *************
' Create the signing key using the function defined above.
Dim signingKey() As Byte, signature As String
signingKey = getSignatureKey(secretKey, dateStamp, region, service)
' Sign the string_to_sign using the signing_key
signature = byte2hex(hmacSha256(signingKey, stringToSign))
' ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
' Put the signature information in a header named Authorization.
Dim authorizationHeader As String
authorizationHeader = _
algorithm + " " + _
"Credential=" + accessKey + "/" + credentialScope + ", " + _
"SignedHeaders=" + signedHeaders + ", " + _
"Signature=" + signature
' ************* SEND THE REQUEST TO AWS! *************
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
http.Open "POST", "https://" + host + uri, False
http.setrequestheader "content-type", contentType
http.setrequestheader "host", host
http.setrequestheader "x-amz-date", amzDate
http.setrequestheader "authorization", authorizationHeader
http.Send requestParameters
' If the result is 403 Forbidden, then clear the current selected credentials.
If http.Status = 403 Then
accessKey = ""
secretKey = ""
End If
' Return the HTTP response back to the calling program.
Set callWebService = http
End Function
Private Function GMT() As Date
Dim dt As Object
Set dt = CreateObject("WbemScripting.SWbemDateTime")
dt.SetVarDate Now
GMT = dt.GetVarDate(False)
Set dt = Nothing
End Function
Private Function date2iso8601(dateTime As Date) As String
date2iso8601 = Format(dateTime, "yyyymmdd\Thhnnss\Z")
End Function
Private Function getSignatureKey(key As String, dateStamp As String, regionName As String, serviceName As String) As Byte()
Dim kDate() As Byte, kRegion() As Byte, kService() As Byte, kSigning() As Byte
kDate = hmacSha256("AWS4" + key, dateStamp)
kRegion = hmacSha256(kDate, regionName)
kService = hmacSha256(kRegion, serviceName)
kSigning = hmacSha256(kService, "aws4_request")
getSignatureKey = kSigning
End Function

Related

HttpClient Digest authentication VB.Net

I'm writing an .NET application that uses HttpClient to connect to the API and retrieve video.
The Documentation provided for "Cookie-based authentication" details the following to login:
Call GET /api/getNonce
In response you'll get a JSON object with realm and nonce (nonce is a session key for this user)
Calculate authentication hash auth_digest, using realm and nonce (see algorithm below)
Call and pass the "auth" parameter in the json request body GET /api/cookieLogin
Server will check authentication and set session cookies
Which then expands on "Calculating Authentication Hash" with the following steps:
Call GET /api/getNonce
In response you'll get a JSON object with realm and nonce
Translate user's username to the lower case
Check the required method ("GET" for HTTP GET requests, "POST" for HTTP POST
requests, "PLAY" for RTSP etc)
digest = md5_hex(user_name + ":" + realm + ":" + password)
partial_ha2 = md5_hex(method + ":")
simplified_ha2 = md5_hex(digest + ":" + nonce + ":" + partial_ha2)
auth_digest = base64(user_name + ":" + nonce + ":" + simplified_ha2)
Here auth_digest is the required authentication hash
I use this Article written in C# as a reference, converted it to VB.NET, change a little bit of code in it according to the requirement above and then implemented it into my program. But I always get an error like this
"The remote server returned an error: (403) Forbidden."
When I debug the program, the problem appears when reading DigestAuthFixer.vb class in GrabResponse Function on this line response = CType(request2.GetResponse(), HttpWebResponse) . I suspect that "auth_digest" didn't return the correct value so the web service to deny the request with a 403 error. My question is how to properly implement digest authentication using VB.Net ? Are there any standard methods or do I have to do it from scratch? Thanks in advance.
This is the code that I use to connect to the API and retrieve the video:
Download HTTPClient
Private Shared _host As String = "http://127.0.0.1:7001"
Private Shared _username As String = "user"
Private Shared _password As String = "password"
Dim auto As Boolean = False
Shared ReadOnly client As HttpClient = New HttpClient()
Public Async Function Download_HttpClient(ByVal cameraID As String _
, ByVal videoDT As String _
, ByVal duration As String _
, ByVal resolution As String _
, ByVal path As String) As Task(Of String)
Dim cookieContainer As New CookieContainer
Dim httpClientHandler As New HttpClientHandler
httpClientHandler.AllowAutoRedirect = True
httpClientHandler.UseCookies = True
httpClientHandler.CookieContainer = cookieContainer
Dim httpClient As New HttpClient(httpClientHandler)
Dim err As Boolean = False
Dim downloadFileName As String
'SET HTTPCLIENT HEADERS
httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)")
'httpClient.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml")
httpClient.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+json,application/json")
httpClient.DefaultRequestHeaders.Add("Accept-Charset", "ISO-8859-1")
Dim downloadId As String = Nothing
Try
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls12 Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls
'GET USER RIGHTS
Dim login_url As String = _host + "/api/getNonce"
Dim login_parameter As String = "?user_name =" + _username + "&password=" + _password
Dim login_response As HttpResponseMessage = Await httpClient.GetAsync(login_url + login_parameter)
Dim login_contents As String = Await login_response.Content.ReadAsStringAsync()
'CALCULATE AUTHENTICATION HASH
Dim dir As String = "/dir/index.html"
Dim url As String = _host + "/api/getNonce"
Dim digest As NUI.DigestAuthFixer = New NUI.DigestAuthFixer(url, _username.ToLower, _password)
Dim auth As String = digest.GrabResponse(dir)
'CALL POST /api/cookieLogin
Dim client As HttpClient = New HttpClient
client.DefaultRequestHeaders.Accept.Add(New System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"))
Dim postData As New List(Of KeyValuePair(Of String, String))
postData.Add(New KeyValuePair(Of String, String)("auth_digest ", auth))
Dim content As New FormUrlEncodedContent(postData)
content.Headers.ContentType = New Headers.MediaTypeHeaderValue("application/x-runtime-guid")
Dim response As HttpResponseMessage = client.PostAsync(New Uri(_host + "/api/cookieLogin"), content).Result
If response.IsSuccessStatusCode Then
MsgBox("POST CookieLogin Successfully")
Else
MsgBox(response)
End If
Catch ex As Exception
MessageBox.Show("Invalid response from the server due to connection limitation or firewall blocking your request")
Return ex.ToString()
End Try
'CREATE DOWNLOAD URL
Dim download_url As String = _host + "/media/" + cameraID + ".mkv"
Dim download_param As String = "?pos=" + videoDT + "&duration=" + duration + "&resolution=" + resolution
Dim downloadFile As String = download_url + download_param
'GET REQUEST AND DOWNLOAD
Dim file_response = Await httpClient.GetAsync(downloadFile)
Dim file_header = file_response.Content.Headers.GetValues("Content-Disposition")
Dim temp_string() As String = file_header.ToArray()
temp_string = temp_string(0).Split("=")
downloadFileName = temp_string(1).Replace("""", "")
Try
Using fs As New FileStream(path & downloadFileName, FileMode.Create)
Await file_response.Content.CopyToAsync(fs)
End Using
Catch ex As Exception
MessageBox.Show("Directory does not exists, please manually change the folder target")
Return ex.ToString()
End Try
Return "Download successfully"
End Function
DigestAuthFixer.vb class
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Security.Cryptography
Imports System.Text.RegularExpressions
Imports System.Net
Imports System.IO
Imports System.Net.Http
Namespace NUI
Public Class DigestAuthFixer
Private Shared _host As String
Private Shared _user As String
Private Shared _password As String
Private Shared _realm As String
Private Shared _nonce As String
Private Shared _qop As String
Private Shared _cnonce As String
Private Shared _cnonceDate As DateTime
Private Shared _nc As Integer
Public Sub New(ByVal host As String, ByVal user As String, ByVal password As String)
_host = host
_user = user
_password = password
End Sub
Private Function CalculateMd5Hash(ByVal input As String) As String
Dim inputBytes As Byte() = Encoding.GetEncoding("ISO-8859-1").GetBytes(input)
Dim hash = MD5.Create().ComputeHash(inputBytes)
Dim sb As New System.Text.StringBuilder()
For Each b In hash
sb.Append(b.ToString("x2"))
Next b
Return sb.ToString()
End Function
Private Function GrabHeaderVar(ByVal varName As String, ByVal header As String) As String
Dim regHeader = New Regex(String.Format("{0}=""([^""]*)""", varName))
Dim matchHeader = regHeader.Match(header)
If matchHeader.Success Then Return matchHeader.Groups(1).Value
Throw New ApplicationException(String.Format("Header {0} not found", varName))
End Function
Private Function GetDigestHeader(ByVal dir As String) As String
Dim digest = CalculateMd5Hash(_user.ToLower + ":" + _realm + ":" + _password)
Dim partial_ha2 = CalculateMd5Hash("GET" + ":")
Dim simplified_ha2 = CalculateMd5Hash(digest + ":" + _nonce + ":" + partial_ha2)
Dim auth_digest = (_user.ToLower + ":" + _nonce + ":" + simplified_ha2)
Return auth_digest
End Function
Public Function GrabResponse(ByVal dir As String) As String
Dim url = _host & dir
Dim uri = New Uri(url)
Dim request = CType(WebRequest.Create(uri), HttpWebRequest)
If Not String.IsNullOrEmpty(_cnonce) AndAlso DateTime.Now.Subtract(_cnonceDate).TotalHours < 1.0 Then
request.Headers.Add("Authorization", GetDigestHeader(dir))
End If
Dim response As HttpWebResponse
Try
response = CType(request.GetResponse(), HttpWebResponse)
Catch ex As WebException
If ex.Response Is Nothing OrElse (CType(ex.Response, HttpWebResponse)).StatusCode <> HttpStatusCode.Unauthorized Then Throw
Dim wwwAuthenticateHeader = ex.Response.Headers("WWW-Authenticate")
_realm = GrabHeaderVar("realm", wwwAuthenticateHeader)
_nonce = GrabHeaderVar("nonce", wwwAuthenticateHeader)
_qop = "auth"
_nc = 0
_cnonce = New Random().[Next](123400, 9999999).ToString()
_cnonceDate = DateTime.Now
Dim request2 = CType(WebRequest.Create(uri), HttpWebRequest)
request2.Headers.Add("Authorization", GetDigestHeader(dir))
response = CType(request2.GetResponse(), HttpWebResponse)
End Try
Dim reader = New StreamReader(response.GetResponseStream())
Return reader.ReadToEnd()
End Function
End Class
End Namespace

Calling SSRS from SSIS: Some the PDF files gets corrupted

I am calling SSRS from SSIS Script task and stores the report in PDF format.
Below is my code in Script task:
Protected Sub SaveFile(ByVal url As String, ByVal localpath As String)
Dim loRequest As System.Net.HttpWebRequest
Dim loResponse As System.Net.HttpWebResponse
Dim loResponseStream As System.IO.Stream
Dim loFileStream As New System.IO.FileStream(localpath, System.IO.FileMode.Create, System.IO.FileAccess.Write)
Dim laBytes(256) As Byte
Dim liCount As Integer = 1
Try
loRequest = CType(System.Net.WebRequest.Create(url), System.Net.HttpWebRequest)
loRequest.Credentials = System.Net.CredentialCache.DefaultCredentials
loRequest.Timeout = 7200
loRequest.Method = "GET"
loResponse = CType(loRequest.GetResponse, System.Net.HttpWebResponse)
loResponseStream = loResponse.GetResponseStream
Do While liCount > 0
liCount = loResponseStream.Read(laBytes, 0, 256)
loFileStream.Write(laBytes, 0, liCount)
Loop
loFileStream.Flush()
loFileStream.Close()
Catch ex As Exception
End Try
End Sub
Public Sub Main()
Dim url, destination, Todaydate, FolderName, DestinationError As String
Try
Todaydate = Dts.Variables("TodayDate").Value.ToString
FolderName = Dts.Variables("FolderName").Value.ToString
destination = Dts.Variables("DestFolder").Value.ToString + "\" + Todaydate + "\" + FolderName + "\" + Dts.Variables("CurrentReport").Value.ToString + "_" + Dts.Variables("CurrentParamID").Value.ToString + "_" + Format(Now, "yyyyMMdd") + ".pdf"
url = "http://server-name/ReportServer?/ReportPath/AUTOMATED/" + Dts.Variables("CurrentReport").Value.ToString + "&rs:Command=Render&Param=" + Dts.Variables("CurrentParamID").Value.ToString + "&rs:Format=PDF"
Dts.Variables("User::GeneratedPDFpath").Value = destination
SaveFile(url, destination)
End Try
Dts.TaskResult = ScriptResults.Success
End Sub
This code works fine and generates report in PDF. But, sometimes it generates PDF file with size of 0 KB. When that PDF is opened, it displays an error for Corrupted File.
P.S. I have used Execute SQL task which stores a resultset. Using that resultset in Foreach Loop container, it initially created folder for today's date and stores the respective PDF in folder.
Would appreciate if someone could provide some help on this.

Upload File to Amazon S3

Let me explain my requirement..
I want to store files uploaded to my app (by clients) to Amazon S3..
File Size ~ 1-10 MB
However, the client interface has to be a REST API
provided by my application. Consequently, after parsing file upload (HTTP POST) request, my application must store the file in S3.
As a result, I have to store file temporarily on disk before uploading to S3..
Is there a workaround? Can I do away with temporary file store on my server.. Please let me know if I am not clear..
EDIT - Is it OK to get byte array from FileItem object and store it rather than the file itself..?
Your whole idea is to avoid I/O right ? you don't need to save the file before doing the upload, you could simple send the array of bytes to amazon REST API.
Here is my sample VB.NET code that do both upload and download:
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Net
Imports System.Security.Cryptography
Imports System.Text
Imports System.Threading.Tasks
Module Module1
Sub Main()
Dim obj As New Program
obj.UploadFile()
'obj.DownloadFile() 'Download Example
End Sub
End Module
Class Program
Private Const KeyId As String = "yourkey"
Private Const AccessKey As String = "your/access"
Private Const S3Url As String = "https://s3.amazonaws.com/"
Public Sub DownloadFile()
Dim bucketName As String = "yourbucket"
Dim FileName As String = "file.png"
Dim timeStamp As String = String.Format("{0:r}", DateTime.UtcNow)
Dim stringToConvert As String = Convert.ToString((Convert.ToString((Convert.ToString("GET" & vbLf + vbLf + vbLf + vbLf + "x-amz-date:") & timeStamp) + vbLf + "/") & bucketName) + "/") & FileName
Dim ae = New UTF8Encoding()
Dim signature = New HMACSHA1() With { _
.Key = ae.GetBytes(AccessKey) _
}
Dim bytes = ae.GetBytes(stringToConvert)
Dim moreBytes = signature.ComputeHash(bytes)
Dim encodedCanonical = Convert.ToBase64String(moreBytes)
' Send the request
Dim request As HttpWebRequest = DirectCast(HttpWebRequest.Create(Convert.ToString((Convert.ToString("https://") & bucketName) + ".s3.amazonaws.com" + "/") & FileName), HttpWebRequest)
'request.ContentType = "application/octet-stream";
request.Headers.Add("x-amz-date", timeStamp)
request.Headers.Add("Authorization", "AWS " + KeyId + ":" + encodedCanonical)
request.Method = "GET"
' Get the response
Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
Dim ReceiveStream As Stream = response.GetResponseStream()
Console.WriteLine(response.StatusCode)
End Sub
Public Sub UploadFile()
Dim fileData = File.ReadAllBytes("C:\file.png")
Dim timeStamp As String = String.Format("{0:r}", DateTime.UtcNow)
Dim stringToConvert As String = (Convert.ToString("PUT" & vbLf + vbLf + "application/octet-stream" & vbLf + vbLf + "x-amz-acl:public-read" + vbLf + "x-amz-date:") & timeStamp) + vbLf + "/celso711/file.png"
'resource
Dim ae = New UTF8Encoding()
Dim signature = New HMACSHA1() With { _
.Key = ae.GetBytes(AccessKey) _
}
Dim bytes = ae.GetBytes(stringToConvert)
Dim moreBytes = signature.ComputeHash(bytes)
Dim encodedCanonical = Convert.ToBase64String(moreBytes)
Dim url = "https://bucket.s3.amazonaws.com/file.png"
Dim request = TryCast(WebRequest.Create(url), HttpWebRequest)
request.Method = "PUT"
request.Headers("x-amz-date") = timeStamp
request.Headers("x-amz-acl") = "public-read"
request.ContentType = "application/octet-stream"
request.ContentLength = fileData.Length
request.Headers("Authorization") = (Convert.ToString("AWS ") & KeyId) + ":" + encodedCanonical
Dim requestStream = request.GetRequestStream()
requestStream.Write(fileData, 0, fileData.Length)
requestStream.Close()
Using response = TryCast(request.GetResponse(), HttpWebResponse)
Dim reader = New StreamReader(response.GetResponseStream())
Dim data = reader.ReadToEnd()
End Using
End Sub
End Class

Attachments using REST WebService and VB.NET

I am currently developing an application using VB.NET in which I am using the REST WebServices. I have been able to do the basics with REST, however, I have not been able to add an attachment (more specifically upload a file, using REST which gets attached). I have done extensive research online, but so far I have not been able to find any working examples in VB.NET. To actually upload the data I use System.Net.WebClient. The following VB.NET code does the important work:
Dim Client As New System.Net.WebClient
Dim postBytes As Byte() = System.Text.Encoding.ASCII.GetBytes(postString)
Client.UploadData(URL, "POST", postBytes)
A simplified version of my URL is as follows:
"../REST/1.0/ticket/" + ticketNumber + "/comment?user=" + userName + "&pass=" + password
Finally, an example of the content that I post is:
postString = "content=Text: RT Test" + vbLf + "Action: Comment" + vbLf + "Attachment: examplefile.jpg" + vbLf + "attachment_1="
As you can see, the postString is converted to bytes and then uploaded to the server. However, I do not know where or how I should be posting the raw attachment itself. The documentation for the service we are specifically using states to use a variable "attachment_1," which I added to the postString variable, but I am not sure what the next step should be. Should the file be converted into bytes and appended to the postBytes variable? I attempted something like this but I received an error saying that no attachment was found for examplefile.jpg.
Thanks for your help!
We could not use Client.UploadData(...) and had to convert the entire post to bytes, starting with the POST fields before the attachment, then the attachment itself, and finally the remainder of the POST fields.
Public Sub AddAttachmentToRT(ByVal url As String, ByVal fileName As String, ByVal filePath As String)
Dim dataBoundary As String = "--xYzZY"
Dim request As HttpWebRequest
Dim fileType As String = "image/jpeg" 'Will want to extract this to make it more generic from the uploaded file.
'Create a POST web request to the REST interface using the passed URL
request = CType(WebRequest.Create(url), HttpWebRequest)
request.ContentType = "multipart/form-data; boundary=xYzZY"
request.Method = "POST"
request.KeepAlive = True
'Write the request to the requestStream
Using requestStream As IO.Stream = request.GetRequestStream()
'Create a variable "attachment_1" in the POST, specify the file name and file type
Dim preAttachment As String = dataBoundary + vbCrLf _
+ "Content-Disposition: form-data; name=""attachment_1""; filename=""" + fileName + """" + vbCrLf _
+ "Content-Type: " + fileType + vbCrLf _
+ vbCrLf
'Convert this preAttachment string to bytes
Dim preAttachmentBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(preAttachment)
'Write this preAttachment string to the stream
requestStream.Write(preAttachmentBytes, 0, preAttachmentBytes.Length)
'Write the file as bytes to the stream by passing its exact location
Using fileStream As New IO.FileStream(Server.MapPath(filePath + fileName), IO.FileMode.Open, IO.FileAccess.Read)
Dim buffer(4096) As Byte
Dim bytesRead As Int32 = fileStream.Read(buffer, 0, buffer.Length)
Do While (bytesRead > 0)
requestStream.Write(buffer, 0, bytesRead)
bytesRead = fileStream.Read(buffer, 0, buffer.Length)
Loop
End Using
'Create a variable named content in the POST, specify the attachment name and comment text
Dim postAttachment As String = vbCrLf _
+ dataBoundary + vbCrLf _
+ "Content-Disposition: form-data; name=""content""" + vbCrLf _
+ vbCrLf _
+ "Action: comment" + vbLf _
+ "Attachment: " + fileName + vbCrLf _
+ "Text: Some description" + vbCrLf _
+ vbCrLf _
+ "--xYzZY--"
'Convert postAttachment string to bytes
Dim postAttachmentBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(postAttachment)
'Write the postAttachment string to the stream
requestStream.Write(postAttachmentBytes, 0, postAttachmentBytes.Length)
End Using
Dim response As Net.WebResponse = Nothing
'Get the response from our REST request to RT
'Required to capture response, without this Try-Catch attaching will fail
Try
response = request.GetResponse()
Using responseStream As IO.Stream = response.GetResponseStream()
Using responseReader As New IO.StreamReader(responseStream)
Dim responseText = responseReader.ReadToEnd()
End Using
End Using
Catch exception As Net.WebException
response = exception.Response
If (response IsNot Nothing) Then
Using reader As New IO.StreamReader(response.GetResponseStream())
Dim responseText = reader.ReadToEnd()
End Using
response.Close()
End If
Finally
request = Nothing
End Try
End Sub

The remote server returned an error: (501) Syntax error in parameters or arguments

I am trying to upload a file to Mainframe machine from my VB.net application. I am getting the following error.
The remote server returned an error: (501) Syntax error in parameters or arguments
Below is my code. Any help will be appreciated.
Dim urlHost As String = "ftp://" + FWFTP.Default.TargetFTPHost.ToString()
Dim url As String = "ftp://" + FWFTP.Default.TargetFTPHost & "/" & FWFTP.Default.TargetFolder & "/" & fileName
Dim clsRequest As System.Net.FtpWebRequest = CType(WebRequest.Create(url), FtpWebRequest)
clsRequest.Credentials = New System.Net.NetworkCredential(FWFTP.Default.FTPUserName.ToString(), FWFTP.Default.FTPPassword.ToString())
clsRequest.Proxy = Nothing
Dim target As New Uri(urlHost)
Dim iphe() As System.Net.IPAddress = System.Net.Dns.GetHostAddresses(target.Host)
If My.Computer.Network.Ping(iphe(0).ToString, 200) = False Then
MsgBox("Ping: " + urlHost + ":" + iphe(0).ToString + " Failed")
'ElseIf FTPSite <> "" Then
End If
clsRequest.KeepAlive = False
clsRequest.UseBinary = False
clsRequest.UsePassive = True
clsRequest.Method = System.Net.WebRequestMethods.Ftp.UploadFile
' read in file...
Dim bFile() As Byte = System.IO.File.ReadAllBytes(fullPath)
' upload file...
Dim clsStream As System.IO.Stream = _
clsRequest.GetRequestStream()
clsStream.Write(bFile, 0, bFile.Length)
clsStream.Close()
clsStream.Dispose()
When you say 'mainframe', it wouldn't be AS/400 by any chance?
http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/6c621557-62af-4d4a-9afd-e5b127c380f4
In particular, see the suggestion by FTPOdyssey in this thread
When you use System.Net.WebClient or FtpWebRequest and you access a Z/Os mainframe if you want to access the following ftp url (notice the # in the password):
ftp://userid:p#ssword#host/pub/data/msg.log
You must do the following:
ftp://userid:%40ssword#host/%2Fpub/data/msg.log
This should work with either download or upload.