FtpWebRequest - Access to IBM AS/400 FTP Server - 501 Character (/) not allowed in object name.
-
Tuesday, November 29, 2005 5:06 PMHello everybody
I'm trying to communicate with an AS/400 FTP Server. As a first try I just execute the WebRequestMethods.Ftp.ListDirectory method, which translates to the NLST FTP command. Doing the same accessing a Microsoft FTP Server or a UNIX FTP Server works fin.
This is the code to get the listing:
Dim Uri As System.Uri = New System.Uri(listUrl)
Dim listRequest As FtpWebRequest = CType(WebRequest.Create(Uri), FtpWebRequest)
listRequest.Credentials = New System.Net.NetworkCredential(User, Password)listRequest.Method = WebRequestMethods.Ftp.ListDirectory
listRequest.KeepAlive = False
listRequest.UseBinary = False
listRequest.UsePassive = False
Dim listResponse As FtpWebResponse = CType(listRequest.GetResponse(), FtpWebResponse)
Reader = New StreamReader(listResponse.GetResponseStream())
Return reader.ReadToEnd()
This is a short trace where the error is occurring (see highlighted section):
System.Net Information: 0 : [5116] FtpWebRequest#8707876::.ctor(ftp://192.168.133.2/mak)
System.Net Information: 0 : [5116] FtpWebRequest#8707876::GetResponse(Method=NLST.)
System.Net Information: 0 : [5116] Associating FtpWebRequest#8707876 with FtpControlStream#50927418
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Received response [220-QTCP at 192.168.133.2. 220 Connection will close if idle more than 15 minutes.]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Sending command [USER TESTUSER]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Received response [331 Enter password.]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Sending command [PASS ********]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Received response [230 TESTUSER logged on.]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Sending command [OPTS utf8 on]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Received response [501 OPTS unsuccessful; specified subcommand not recognized.]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Sending command [PWD]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Received response [257 "QGPL" is current library.]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Sending command [CWD QGPL/]
System.Net Information: 0 : [5116] FtpControlStream#50927418 - Received response [501 Character (/) not allowed in object name.]
System.Net Information: 0 : [5116] FtpWebRequest#8707876::(Releasing FTP connection#50927418.)
System.Net Error: 0 : [5116] Exception in the FtpWebRequest#8707876::GetResponse - The remote server returned an error: (501) Syntax error in parameters or arguments.
System.Net Error: 0 : [5116] at System.Net.FtpWebRequest.SyncRequestCallback(Object obj)
at System.Net.FtpWebRequest.RequestCallback(Object obj)
at System.Net.CommandStream.Abort(Exception e)
at System.Net.FtpWebRequest.FinishRequestStage(RequestStage stage)
at System.Net.FtpWebRequest.GetResponse()
The AS/400 FTP Server dose not allow a "/" for changing directories. The server would accept an alternative filenameformat (quote site namefmt 1) which would accept slashes.
Is there a write method that I could override to filter the CWD command and change it?
Or is there a way to insert the "site namefmt 1" command?
I hope someone can help me with this problem!
All Replies
-
Tuesday, November 29, 2005 5:07 PMModerator
I will look into this.
Is this server reacheable over the internet? -
Tuesday, November 29, 2005 5:12 PMSorry. The server is not reachable over the internet.
-
Tuesday, November 29, 2005 5:24 PMModeratorOK we will look into this
-
Wednesday, November 30, 2005 7:49 AMModerator
Currently FtoWebRequest does not support quote and I cannot think of a way you'll be able to overide the method without exposing our code -
Friday, September 01, 2006 7:46 PM
Has anything changed regarding this behavior since the last post? (11-30-2005)
Has Microsoft come up with any workarounds, so that we can connect to AS/400, VMS & MVS FTP servers using .Net?
-
Thursday, September 28, 2006 5:44 PMDid anyone ever answer this? I am trying to do the same thing!!!!
-
Thursday, September 28, 2006 6:08 PM
Katie,
I came to a complete deadend with Microsoft on this. Simply not supported and no workarounds.
I'm currently converting my code over to using WeOnlyDo - wodFtpDLX.NET component.
It's relatively inexpensive and was the only 3rd party component that I could find to handle the SSH protocol in addition to all the variations of SSL.
I am currently working with the developers of this software, so that their component correctly parses directory stuctures for AS/400, MVS & VMS.
Since I already had the parsing logic in my program, I kept it in as a precautionary fail-over. The main thing I was after anyway, was for the component to handle making the connections via the correct protocol.
Here's a link if you want to check into this software to see if it meets your needs.
http://www.weonlydo.com/index.asp?showform=FtpDLX.NET
Hopefully this will save you some time and frustration.
Good Luck, Dave
-
Friday, September 29, 2006 12:13 AMModerator
Unfortunately your scenario is still not supported for FtpWebRequest. We will work on getting this fixed in a future release
Mariya
-
Friday, September 29, 2006 8:47 AM
As you can see from Mariya’s message (28.09.2006) FtpWebRequest dose still not support the special directory and file structure of AS/400.
As a workaround I start FTP.EXE in a separate process. It is not the nicest way to do the work but it works. Of course it dose not support any secure transport but as I work over VPN it is not necessary.
This is an example how to retrieve files from the FTP-Server:
Public Class FTPExample
'These variables hold the result from Output and Error steams
'from the process
Private Shared FTPOutput As System.Text.StringBuilder = Nothing
Private Shared FTPError As System.Text.StringBuilder = Nothing
Public Shared Function FTPGet(ByVal RemotePath As String, _
ByVal LocalPath As String, _
ByVal FileNames() As String) As Boolean
'FTP Process initalization
Dim FTPProcess As New Process()
FTPProcess.StartInfo.FileName = "ftp.exe"
'UseShellExecute to false to enable redirection
'of INPUT/OUTUT/ERROR
FTPProcess.StartInfo.UseShellExecute = False
'Hide window
FTPProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
'Redirection of OUTPUT- and ERROR-Streams
FTPProcess.StartInfo.StandardOutputEncoding = _
System.Text.Encoding.GetEncoding(850)
FTPProcess.StartInfo.StandardErrorEncoding = _
System.Text.Encoding.GetEncoding(850)
FTPProcess.StartInfo.RedirectStandardOutput = True
FTPProcess.StartInfo.RedirectStandardError = True
'Creation of OUTPUT- and ERROR-StringBuilder
FTPOutput = New System.Text.StringBuilder()
FTPError = New System.Text.StringBuilder()
'Eventhandler for OUTPUT- and ERROR-Streams
AddHandler FTPProcess.OutputDataReceived, _
AddressOf FTPOutputHandler
AddHandler FTPProcess.ErrorDataReceived, _
AddressOf FTPErrorHandler
'Redirection of INPUT
FTPProcess.StartInfo.RedirectStandardInput = True
'This process argument forces the FTP.EXE to execute first
'the file "FTPLogin".
'Be aware of the current directory when you use a
'relative file path or use absolute file path.
'in our case it contains in the first line an open statement
'in the second the username and in the third the password
'example:
'open 192.168.1.1
'joe
'12345
FTPProcess.StartInfo.Arguments = "-s:FTPLogin"
'Starting the process
FTPProcess.Start()
'Creating reference to Input-Writer
Dim FTPStreamWriter As System.IO.StreamWriter = _
FTPProcess.StandardInput
'Start reading asynchronously
FTPProcess.BeginOutputReadLine()
FTPProcess.BeginErrorReadLine()
'Sending commands to FTP-Process
FTPStreamWriter.WriteLine("DEBUG")
FTPStreamWriter.WriteLine("PROMPT")
FTPStreamWriter.WriteLine("QUOTE SITE LISTFMT 0")
FTPStreamWriter.WriteLine("QUOTE SITE NAMEFMT 0")
FTPStreamWriter.WriteLine("LCD " + LocalPath)
FTPStreamWriter.WriteLine("CD " + RemotePath)
FTPStreamWriter.WriteLine("ASCII")
If FileNames Is Nothing Then
'retrieve all files from the remote path if nothing is defined
FTPStreamWriter.WriteLine("MGET *.*")
Else
'retrieve the selected files from the remote path
For Each FileName As String In FileNames
FTPStreamWriter.WriteLine("GET " + _
System.IO.Path.GetFileName(FileName))
Next
End If
FTPStreamWriter.WriteLine("QUIT")
'Waiting for process termination
FTPProcess.WaitForExit()
'Close Input-Writer
FTPStreamWriter.Close()
'process OUTPUT/ERROR results
If FTPError.Length = 0 Then
Debug.WriteLine("FTPProcess Error: <empty>")
Else
Debug.WriteLine("FTPProcess Error: " + FTPError.ToString)
End If
If FTPOutput.Length = 0 Then
Debug.WriteLine("FTPProcess Output: <empty>")
Else
Debug.WriteLine("FTPProcess Output: " + FTPOutput.ToString)
End If
'Removing Eventhandlers for Output and Error-streams
RemoveHandler FTPProcess.OutputDataReceived, _
AddressOf FTPOutputHandler
RemoveHandler FTPProcess.ErrorDataReceived, _
AddressOf FTPErrorHandler
'Closing process object
FTPProcess.Close()
FTPProcess.Dispose()
FTPProcess = Nothing
'doing some error handling
Return (FTPError.Length = 0)
End Function
Private Shared Sub FTPOutputHandler(ByVal sendingProcess As Object, _
ByVal outLine As DataReceivedEventArgs)
'Collecting Output
If Not String.IsNullOrEmpty(outLine.Data) Then
FTPOutput.Append(outLine.Data + Environment.NewLine)
End If
End Sub
Private Shared Sub FTPErrorHandler(ByVal sendingProcess As Object, _
ByVal outLine As DataReceivedEventArgs)
'Collecting Error
If Not String.IsNullOrEmpty(outLine.Data) Then
FTPError.Append(outLine.Data + Environment.NewLine)
End If
End Sub
End Class
I hope this helps you a little bit.
-
Monday, October 02, 2006 9:26 PM
Thanks for the update... I've been trying to do the same thing and running into this error. Thanks for the helpful information.
FYI, we've use the Xceed .NET FTP component to access the AS400 programmatically. Its fully featured, but its not cheap.
http://www.xceedsoft.com/products/FtpNet/index.aspx
-
Monday, October 23, 2006 9:16 PMI have the same problem with accessing MVS. Took me forever to try and fail. In the end I found this post and lo-and-behold! It doesn't work!!!

-
Tuesday, October 31, 2006 4:02 AMForgive me if this is a stupid question, but is wininet no longer an option after VB6?
-
Wednesday, March 07, 2007 4:18 PMGreat Work-Around, Thanks!
-
Tuesday, March 20, 2007 8:02 PM
I am dumbfounded that Microsoft let this slide! Entire classes of FTP servers can't be accessed all because of a path separator!! Microsoft, an easy fix to this would be to expose a path separator property on the FtpWebRequest class, that way people whose server doesn't support a / as the path separator can specify what it is. That, or don't insist on appending a slash to the end of the path.
When a simple program like ftp that's been built into the OS for years can do something that complex .NET can't do it's a shame, especially when the only problem is a slash character... The quality of Microsoft products is going down, erm, getting even worse.
-
Wednesday, March 21, 2007 12:06 AM
This is really helpful. However, I noticed that it does not truely work in interactive mode. The system does not process my commands as I write them via the stream writer (even if I set AutoFlush). It does nothing until the WaitForExit. And then I get my response back all at once.
Is there any way to make this code more responsive?
-
Tuesday, March 27, 2007 1:49 PM
Another option in terms of FTP, and the option I ultimately went with, is Indy. This is a huge free library, around 2.5 MB, of networking protocols. It can handle FTP, HTTP, IMAP, etc. This is an example of the code I used to do my FTP.
Code SnippetIndy.Sockets.
FTP ftp = new Indy.Sockets.FTP();try
{ftp.Host =
"eagle";ftp.Username =
someusername;ftp.Password =
somepassword;ftp.Connect();
if(!String.IsNullOrEmpty(directory))ftp.ChangeDir(directory);
ftp.Put(decryptedPathname, filename,
false);}
catch
(Exception x) { this.MessageBox(String.Format(Messages.TransferError, someusername, x.Message));okay =
false;}
finally
{ftp.Quit();
}
-
Friday, October 26, 2007 2:19 PM
Hello,
Try configuring the iSeries FTP server to use *PATH in name format and *HOMEDIR as default directoy.
You will need SECOF rights to access FTP configuration:
CHGFTPA NAMEFMT(*PATH) CURDIR(*HOMEDIR)
Hope this helps you a little bit.
-
Friday, October 26, 2007 6:32 PM
Unfortunately, I cannot ask my customers to change their FTP parameters. But thanks for the workaround. I hope it helps the others here. And I hope MS addresses the issue.
-
Tuesday, December 25, 2007 2:05 PMHi,
I've the same problem as posted here, but the strange thing that the code succedded in putting file via ftp to the AS400 from the local machine (Windows XP, IIS 5), and it failed when we put the site on the production web server (Windows Server 2003, IIS 6).
I've tried the AS400 configuration on the AS400 test machine, and it worked successfully, but the problem that we can't change the ftp directory of the AS400 production machine as this will lead to changing all the running applications
My code is as shown below:
public static void FTPPutFile(string strFileName, string strFTPPath)
{
try
{
string strFilePath = ConfigurationManager.AppSettings["XMLPathOUT"] + strFileName;
string URI = strFTPPath + strFileName;
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(URI);
ftp.Credentials = new NetworkCredential(ConfigurationManager.AppSettings["AS400UserName"], ConfigurationManager.AppSettings["AS400Password"]);ftp.KeepAlive = false;
ftp.UseBinary = true;
ftp.Proxy = null;
//Write
ftp.Method = WebRequestMethods.Ftp.UploadFile;
Stream stream = ftp.GetRequestStream();
FileStream fs = new FileStream(strFilePath, FileMode.Open);
byte[] buffer = new byte[(int)fs.Length];
fs.Read(buffer, 0, buffer.Length);
stream.Write(buffer, 0, buffer.Length);
fs.Close();
stream.Close();
}
catch (Exception ex)
{
clsHelperMethods.HandleError(ex);
throw (ex);
}
} -
Thursday, January 10, 2008 11:02 PM
I was having the same problem trying to connect to an IBM MVS server...after reading these posts I thought it was hopeless...but I finally got something to work for both uploading and downloading files:
ftp://myFTPsite/%2F%27path.filename%27 - I hope this works for others.
-
Tuesday, April 15, 2008 8:57 AM
Thanks FTPOdyssey, you really saved my day yesterday, I also had to connect to a MVS server via FTPWebRequest and after almost giving up, I finally found your post here and your solution worked! Thanks!!
-
Tuesday, May 06, 2008 5:19 PM
Hello FTPOdyssey,
I am getting this error 501 and wanted to know what you have tried out. The link that you have given doesnt seem to work.
thanks
VJ
-
Tuesday, May 06, 2008 6:17 PM
Here is how I used that statement:
Dim
request As FtpWebRequest = WebRequest.Create("ftp://" & strFTPsite & "/%2F'" & strHostFile & "'")Replace the strFTPsite with the FTP site you are trying to connect to, and the strHostFile with the path and filename like this: mypath.myfile.
- Proposed As Answer by sfibich Wednesday, July 15, 2009 3:15 PM
-
Saturday, May 17, 2008 11:50 AM
Thanks for the reply. Unfortunately didnt work in my case. I used a batch file instead. Thanks once again
VJ
-
Thursday, June 12, 2008 5:13 PM
We have the same problem. We solve it using an open source library called edtFTPnet. You can show it in http://www.enterprisedt.com/products/edtftpnet/overview.html
I write you a little part of our source code. It works great with AS/400 FTP.
string ftpUserID = "anyUser"; string ftpPassword = "xxxxxxxxxxxx"; string ftpServerIP = "as400.host"; private void Form1_Load(object sender, EventArgs e) { int i; FTPClient ftp = new FTPClient(); ftp.RemoteHost = ftpServerIP; ftp.Connect(); ftp.User(ftpUserID); ftp.Password(ftpPassword); ftp.ChDir("PERSONAL_DIR"); string[] files=ftp.Dir(); for(i=0;i<=files.Length-1;i++) { MessageBox.Show(files[i]); } }
The traces are:
--------------------- AS400 .NET STANDARD TRACE -----------------------
220-QTCP at COMPANY.
220 Connection will close if idle more than 5 minutes.
USER anyuser
331 Enter password.
PASS xxxxxxxxxx
230 ANYUSER logged on.
OPTS utf8 on
501 OPTS unsuccessful; specified subcommand not recognized.
PWD
257 "QGPL" is current library.
CWD QGPL/
501 Character (/) not allowed in object name.
221 QUIT subcommand received.
--------------------- AS400 edtFTPnet TRACE ----------------------
220-QTCP at COMPANY.
220 Connection will close if idle more than 5 minutes.
USER anyuser
331 Enter password.
PASS xxxxxxxxxx
230 ANYUSER logged on.
PWD
257 "QGPL" is current library.
CWD PERSONAL_DIR
250 "PERSONAL_DIR" is current library.
PASV
227 Entering Passive Mode (171,18,1,8,282,33).
NLST
125 List started.
250 List completed.
Bye ;).- Proposed As Answer by Marcos Guevara O_ Thursday, June 12, 2008 5:15 PM
- Edited by Marcos Guevara O_ Thursday, June 12, 2008 5:19 PM personal information
-
Thursday, July 10, 2008 3:25 PMThank you FTPOdyssey, works like a charm, no open source garbage necessary.
- Edited by Chris H E Thursday, July 10, 2008 3:26 PM moded
-
Monday, September 08, 2008 10:11 PMLimason showed a correct solution for this using the native FTP client via the Process class. All legal FTP commands are supported.
There is one other solution which no body showed, that's writing a native socket FTP client class especially for the AS/400 and or Mainframe. It wouldn't take that long to do and could be a tad more robust as well...
I also believe on the AS/400 that the system admin's can configure FTP to be in Namefmt 1 as the default as well.
Javaman -
Wednesday, October 29, 2008 9:33 PM
Has anyone ever found a solution for sending QUOTE or SITE commands through the FTPWebRequest object?
-
Thursday, November 20, 2008 4:37 PMDaveWendt, did you ever get WeOnlyDo to work with AS/400?
-
Wednesday, July 15, 2009 3:27 PMThis solution works for me, if you set the URL to ftp://serverName/%2F it will drop you into the Root, if you are not allow to access the root you must know what exact static path you are able to access. (At least in my case thats how it is working)
Thanks for the tip! -
Thursday, August 20, 2009 6:00 AMThanks Marcos,
Your solution solved this problem for me! My code is actually simpler with edtFTPnet than it was with FTPWebRequest -
Saturday, July 17, 2010 8:58 AM
Hi,
I am having the same problem with AS400.
When I am opening the above link it showing some error , can you please post the code one more time ASAP.
please its urgent...
thanks in advance
-
Friday, October 22, 2010 3:20 PM
I was having the same problem connecting to an AS/400. I tried different ftp web requests, and they all raised a WebException. A few things that helped me out - I followed sfibich's suggestion and set my uri to ftp://10.1.1.1/%2f, but that still raised a WebException. In the exception handler I did this:
Catch exWeb As WebException clsShared.PostMsg("WebException error in " & "FtpPrintWorkingDirectory: " & exWeb.ToString, 1) bReturn = False Try Dim ftpResp As FtpWebResponse ftpResp = DirectCast(exWeb.Response, FtpWebResponse) clsShared.PostMsg("WebException error in " & "FtpPrintWorkingDirectory with StatusCode [" & ftpResp.StatusCode & "], StatusDescription [" & ftpResp.StatusDescription & "]", 1) Catch ex2 As Exception ' do nothing End TryThe ftpWebResponse object in the exception allowed me to see the error returned from the ftp server. In my case the StatusDescription was "501 Character (/) not allowed in object name.<CR><LF>". Now I was finally getting the same error that started this thread. So I started playing with the URI class, and using the URIBuilder. I ended up with this method for building the uri in my ftpPut function:
ub = New UriBuilder ub.Scheme = Uri.UriSchemeFtp ub.Host = 10.1.1.1/%2f ub.UserName = UserName ub.Password = Password ub.Path = FtpPathName & "/" & RemoteFile clsShared.PostMsg("Uri is [" & ub.Uri.ToString & "]", 9)I was trying to avoid using forward slashes as much as possible. There was a lot of trial and error here. I kept printing out the uri string so I could see what worked and what didn't. The correct uri for the UploadFile ftp method was ftp://USER:PASS@10.1.1.1///folder/FileName.tmp. I would have never guessed that three forwad slashes was the solution.
I am also setting the Credentials property of the ftpRequest with the same user and pass. I know this is redundant, but it's working so I'm not going to mess with it.
-
Wednesday, April 27, 2011 5:06 PMFantastic - thanks
-
Monday, August 08, 2011 1:15 PM
Hi All,I've been searching and trying quite a lot to get ftpwebrequest working with AS/400...
I found that you just need to specify the right path (syntax) to get access to both, IFS and library system:IFS:ftp://10.5.1.20// [folder/file.ext]Library System:ftp://10.5.1.20//qsys.lib [/somelib.lib/somefile.file[/somemember.mbr]]Just try the path in your browser and you'll see...Uri ur = new Uri("ftp://10.5.1.20//<<folder>>"); // --- or --- Uri ur = new Uri("ftp://10.5.1.20//qsys.lib/<<library>>.lib"); FtpWebRequest wr = (FtpWebRequest)FtpWebRequest.Create(ur); wr.Credentials = new NetworkCredential("<<user>>","<<password>>"); wr.UsePassive = true; wr.UseBinary = true; wr.KeepAlive = true; try { wr.Method = WebRequestMethods.Ftp.ListDirectory; WebResponse rs = wr.GetResponse(); Stream st = rs.GetResponseStream(); StreamReader sr = new StreamReader(st); textBox1.Text = sr.ReadToEnd(); sr.Close(); st.Close(); rs.Close(); } catch (Exception se) { MessageBox.Show(se.Message); } -
Thursday, September 15, 2011 10:38 PM
Like Robert, I found the double-forward-slash is needed if your path contains a bunch of directories.
For me, I needed to FTP a file to the IFS directory - way in there, so my path was something like:
//usr//local//wu-ftpd//ftp//XAMA//reportingreq/test.xml
My code looks something like this, given that:
mServer = ftp://server
mUser and mPass are strings passed into the method
local = text.xml --- the local file
remote = //usr//local//wu-ftpd//ftp//XAMA//reportingreq/test.xml
(remote contains the full path including the filename)try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(mServer + remote); //uri
request.UseBinary = mUseBinary; //true
request.EnableSsl = mUseSsl; //false
request.UsePassive = true;
request.KeepAlive = true;
request.Credentials = new NetworkCredential(mUser, mPass);
request.Method = WebRequestMethods.Ftp.UploadFile;//local is the file on my local directory, the one I am "putting" - it's a string.
StreamReader sourceStream = new StreamReader(local, Encoding.Default);
byte[] fileContents = Encoding.Default.GetBytes(sourceStream.ReadToEnd());
sourceStream.Close();
request.ContentLength = fileContents.Length;
Stream requestStream = request.GetRequestStream(); //Connect...
requestStream.Write(fileContents, 0, fileContents.Length); //Here is where we send the data/file
requestStream.Close();
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
_log.InfoFormat("Upload File Complete, status {0}", response.StatusDescription);
response.Close();
}
catch (Exception e)
{
_log.Error(e.Message);
bRet = false;
}
- Edited by JasHolt Thursday, September 15, 2011 10:41 PM Made sure paths were identical in example
-
Friday, September 30, 2011 7:26 PM
Thanks man! This helped. To reciprocate, here is my object oriented class to try encapsulating the basic functionality. I'm sure I will tweak it more, but this exposes get, put, change directory, and list directory.
Public Class MyFTP
Const TIMEOUT_SECONDS As Integer = 15
Private mDomain As String
Private mWorkingFolder As String
Private mUserID As String
Private mPassword As String
Private ReadOnly Property tempFileName() As String
Get
Return mWorkingFolder & "temp.txt"
End Get
End Property
Private ReadOnly Property loginFileName() As String
Get
Return mWorkingFolder & "login.txt"
End Get
End Property
Public Sub New(ByVal Domain As String, ByVal WorkingFolder As String, _
ByVal UserID As String, ByVal Password As String)
mUserID = UserID
mWorkingFolder = WorkingFolder
If Not mWorkingFolder.EndsWith("\") Then
mWorkingFolder = mWorkingFolder & "\"
End If
mPassword = Password
mDomain = Domain
Call DeleteTempFiles()
Call InitializeConnection()
End Sub
Private Sub DeleteTempFiles()
If IO.File.Exists(tempFileName) Then
Call IO.File.Delete(tempFileName)
End If
If IO.File.Exists(loginFileName) Then
Call IO.File.Delete(loginFileName)
End If
End Sub
'These variables hold the result from Output and Error steams
'from the process
Private Shared mFTPOutput As System.Text.StringBuilder = Nothing
Public ReadOnly Property FTPOutput() As String
Get
Return mFTPOutput.ToString
End Get
End Property
Private Shared mFTPError As System.Text.StringBuilder = Nothing
Public ReadOnly Property FTPError() As String
Get
Return mFTPError.ToString
End Get
End Property
Private mFtpStreamWriter As System.IO.StreamWriter
Private mConnection As Process = Nothing
Private Sub SendWithWait(ByVal input As String)
Dim outputLength As Integer = mFTPOutput.Length
mFtpStreamWriter.WriteLine(input)
mFtpStreamWriter.Flush()
'uses the confirmation message (sent by having DEBUG on) to know that the transfer is complete
Dim waitStart As Date = Now
Do
Call Threading.Thread.Sleep(100)
Loop Until mFTPOutput.Length > outputLength _
Or (Now.Subtract(waitStart)).TotalSeconds >= TIMEOUT_SECONDS
If (Now.Subtract(waitStart)).TotalSeconds >= TIMEOUT_SECONDS Then
Throw New Exception("Command timed out:" & vbCrLf & input)
End If
End Sub
Private Sub Send(ByVal input As String)
SendWithWait(input)
End Sub
Private Shared Sub FTPOutputHandler(ByVal sendingProcess As Object, ByVal outLine As DataReceivedEventArgs)
'Collecting Output
If Not String.IsNullOrEmpty(outLine.Data) Then
mFTPOutput.Append(outLine.Data + Environment.NewLine)
End If
End Sub
Private Shared Sub FTPErrorHandler(ByVal sendingProcess As Object, _
ByVal outLine As DataReceivedEventArgs)
'Collecting Error
If Not String.IsNullOrEmpty(outLine.Data) Then
mFTPError.Append(outLine.Data + Environment.NewLine)
End If
End SubPrivate Sub InitializeConnection()
Dim thisFtpProcess As New Process()
thisFtpProcess.StartInfo.FileName = "ftp.exe"'UseShellExecute to false to enable redirection
'of INPUT/OUTUT/ERROR
thisFtpProcess.StartInfo.UseShellExecute = False'Hide window
thisFtpProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden'Redirection of OUTPUT- and ERROR-Streams
thisFtpProcess.StartInfo.StandardOutputEncoding = System.Text.Encoding.GetEncoding(850)
thisFtpProcess.StartInfo.StandardErrorEncoding = System.Text.Encoding.GetEncoding(850)
thisFtpProcess.StartInfo.RedirectStandardOutput = True
thisFtpProcess.StartInfo.RedirectStandardError = True'Creation of OUTPUT- and ERROR-StringBuilder
mFTPOutput = New System.Text.StringBuilder()
mFTPError = New System.Text.StringBuilder()'Eventhandler for OUTPUT- and ERROR-Streams
AddHandler thisFtpProcess.OutputDataReceived, AddressOf FTPOutputHandler
AddHandler thisFtpProcess.ErrorDataReceived, AddressOf FTPErrorHandler'Redirection of INPUT
thisFtpProcess.StartInfo.RedirectStandardInput = True'create temporary login file
Dim loginText As New System.Text.StringBuilder
Call loginText.AppendLine("open " & mDomain)
Call loginText.AppendLine(mUserID)
Call loginText.AppendLine(mPassword)
Call loginText.AppendLine("CD ..")
Call loginText.AppendLine("DEBUG")
Call IO.File.WriteAllText(loginFileName, loginText.ToString)
thisFtpProcess.StartInfo.Arguments = "-s:" & loginFileName'Starting the process
Call thisFtpProcess.Start()'Creating reference to Input-Writer
mFtpStreamWriter = thisFtpProcess.StandardInput()'Start reading asynchronously
Call thisFtpProcess.BeginOutputReadLine()
Call thisFtpProcess.BeginErrorReadLine()mConnection = thisFtpProcess
'start connection to server
'Send("OPEN " & mDomain)
'send user & password
'Send(mUserID)
'Send(mPassword)
Send("ASCII")
'set local path to the "working folder"
Send("LCD " + mWorkingFolder)
End Sub
Public Sub CloseConnection()
If mConnection Is Nothing Then
Throw New Exception("FTP connection not open")
End If
Send("QUIT")'Waiting for process termination
mConnection.WaitForExit()'Close Input-Writer
mFtpStreamWriter.Close()'process OUTPUT/ERROR results
If FTPError.Length = 0 Then
Debug.WriteLine("FTPProcess Error: <empty>")
Else
Debug.WriteLine("FTPProcess Error: " + FTPError.ToString)
End If
If FTPOutput.Length = 0 Then
Debug.WriteLine("FTPProcess Output: <empty>")
Else
Debug.WriteLine("FTPProcess Output: " + FTPOutput.ToString)
End If'Removing Eventhandlers for Output and Error-streams
RemoveHandler mConnection.OutputDataReceived, AddressOf FTPOutputHandler
RemoveHandler mConnection.ErrorDataReceived, AddressOf FTPErrorHandler'Closing process object
mConnection.Close()
mConnection.Dispose()
mConnection = NothingCall DeleteTempFiles()
'final error handling
If FTPError.Length <> 0 Then
Throw New Exception(FTPError.ToString)
End If
End Sub
Public Sub SetRemotePath(ByVal remotePath As String)
Send("CD " + remotePath)
End SubPublic Function GetFile(ByVal remoteFileName As String) As String
'send FTP command to get the file
Call SendWithWait("GET """ & remoteFileName & """ """ & tempFileName & """")'read the file text into memory
Dim contents As String = IO.File.ReadAllText(tempFileName)'clean up - delete the temporary file that was just created
Call IO.File.Delete(tempFileName)Return contents
End Function
Public Sub PutFile(ByVal contents As String, ByVal remoteFileName As String)
'write the file text from memory
Call IO.File.WriteAllText(tempFileName, contents)'send FTP command to put the file
Call SendWithWait("PUT """ & tempFileName & """ """ & remoteFileName & """")'clean up - delete the temporary file that was just created
Call IO.File.Delete(tempFileName)
End Sub
Public Sub ChangeDirectory(ByVal newRemoteDirectory As String)
'send FTP command to get the file
Call SendWithWait("CD " & newRemoteDirectory)
End Sub
Private Enum DirectoryStructure
PDS = 1
DataSet = 2
End Enum
''' <summary> Note that this does not always work with complex filters (more than one asterisk) </summary>
Public Function GetDirectoryContents(Optional ByVal fileFilter As String = "") As String()
Dim outputLength As Integer = mFTPOutput.Length
'send FTP command to get the directory list
Call SendWithWait("DIR " & fileFilter)
Call SendWithWait("NOOP")Dim waitStart As Date = Now
Dim newOutput As String
Dim outputLines As String()
Do
newOutput = mFTPOutput.ToString.Substring(outputLength)
outputLines = newOutput.Replace(vbCrLf, vbLf).Split(CChar(vbLf))
'NOOP gets back the "Invalid command." response
'(and the last line is always empty string, hence the -2)
If (Now.Subtract(waitStart)).TotalSeconds >= TIMEOUT_SECONDS Then
Throw New Exception("Unable to interpret output of directory listing:" & vbCrLf & newOutput)
End If
Loop Until outputLines(outputLines.Length - 2) = "Invalid command."Dim headerLine As String
Dim listingStructure As DirectoryStructure
headerLine = outputLines(2)
If headerLine.StartsWith(" Name") Then
'this is a PDS. Output will look something like this:
'---> PORT 10,5,16,140,16,122
'---> LIST
' Name VV.MM Created Changed Size Init Mod Id
'#CONCLRA 01.00 2001/03/19 2001/03/19 11:36 1 1 0 #CONCLR
'#KC99A01 01.01 2009/05/14 2009/05/14 09:09 3523 3835 0 KRCRAN
'PAMA103
'PAMA106
'PAMA604 01.01 2011/06/12 2011/06/12 10:52 232 178 0 RXSREE
'PAPA99A 01.85 2006/11/03 2011/09/26 09:28 1554 0 0 RXSREE
'PAPA99AA 01.50 2001/03/26 2004/01/15 07:58 1334 1221 0 MWCOPE
'PAPA99C 01.38 2001/04/09 2006/02/08 13:51 647 581 0 PARITT
'(last line blank)
listingStructure = DirectoryStructure.PDS
ElseIf headerLine.StartsWith("Volume") Then
'this is a list of datasets. Output will look something like this:
'---> PORT 10,5,16,140,16,122
'---> LIST
'Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname
'TSOPK1 3390 2011/07/29 1 8 FB 80 4560 PO BMCCAT.JCL
'TSOPK1 3390 2011/09/29 2 4 VB 4092 4096 PS BMCCAT.SQL
'TSOPK1 3390 2011/09/30 16 240 FB 80 4560 PO JCL
'TSOPK1 3390 2011/05/12 1 15 FB 80 4560 PO JCLE
'TSOPK1 3390 2011/09/30 1 1 FB 80 27920 PS LOGON.CLIST
'(last line blank)
listingStructure = DirectoryStructure.DataSet
ElseIf headerLine = "No members found." Then
Dim emptyStringArray As String()
ReDim emptyStringArray(-1)
Return emptyStringArray
Else
Throw New Exception("Unable to interpret output of directory listing:" & vbCrLf & newOutput)
End IfConst headerRows As Integer = 3
Const trailerRows As Integer = 2
Dim fileNames As String()
ReDim fileNames(outputLines.Length - headerRows - trailerRows - 1)For i As Integer = 0 To fileNames.Length - 1
Dim thisLine As String = outputLines(i + headerRows)
If listingStructure = DirectoryStructure.PDS Then
thisLine = thisLine.Substring(0, 8).Trim
ElseIf listingStructure = DirectoryStructure.DataSet Then
thisLine = thisLine.Substring(56).Trim
End If
fileNames(i) = thisLine
NextReturn fileNames
End Function
End Class -
Friday, October 21, 2011 4:03 PM
Since this thread has helped us to resolve our problems uploading files with the FTPWebRequest to AS/400, here is our solution:
We first changed our url to the following format:
ftp://[ip]/%2F/QSYS.LIB[/some other.LIBs]/test.xml
%2F sends you to the root as explained by other posts here. From there we navigate through some libraries to the destination. This worked well until we got to the uploading part of the FTP as you can see in this trace log file
System.Net Information: 0 : [3664] FtpWebRequest#18796293::(Releasing FTP connection#34948909.)
System.Net Verbose: 0 : [3664] WebRequest::Create(ftp://10.1.1.10///QSYS.LIB/TESTLIB.LIB/test.xml)
System.Net Information: 0 : [3664] FtpWebRequest#46104728::.ctor(ftp://10.1.1.10///QSYS.LIB/TESTLIB.LIB/test.xml)
System.Net Verbose: 0 : [3664] Exiting WebRequest::Create() -> FtpWebRequest#46104728
System.Net Verbose: 0 : [3664] FtpWebRequest#46104728::GetRequestStream()
System.Net Information: 0 : [3664] FtpWebRequest#46104728::GetRequestStream(Method=STOR.)
System.Net Information: 0 : [3664] Associating FtpWebRequest#46104728 with FtpControlStream#12289376
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Received response [220-QTCP at AS400.
220 Connection will close if idle more than 5 minutes.]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Sending command [USER TESTUSER]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Received response [331 Enter password.]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Sending command [PASS ********]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Received response [230 TESTUSER logged on.]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Sending command [OPTS utf8 on]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Received response [501 OPTS unsuccessful; specified subcommand not recognized.]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Sending command [PWD]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Received response [257 "QGPL" is current library.]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Sending command [CWD //QSYS.LIB/TESTLIB.LIB/]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Received response [250-NAMEFMT set to 1.
250 "/QSYS.LIB/TESTLIB.LIB" is current library.]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Sending command [TYPE I]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Received response [200 Representation type is binary IMAGE.]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Sending command [PASV]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Received response [227 Entering Passive Mode (10,1,1,10,102,233).]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Sending command [STOR test.xml]
System.Net Information: 0 : [3664] FtpControlStream#12289376 - Received response [501 Unknown extension in database file name.]
System.Net Information: 0 : [3664] FtpWebRequest#46104728::(Releasing FTP connection#12289376.)
System.Net Error: 0 : [3664] Exception in the FtpWebRequest#46104728::GetRequestStream - The remote server returned an error: (501) Syntax error in parameters or arguments.So we changed test.xml to test.FILE instead and it worked! Instead of the indicated error message we got
System.Net Information: 0 : [3664] FtpControlStream#55915408 - Sending command [STOR test.FILE]
System.Net Information: 0 : [3664] FtpControlStream#55915408 - Received response [150 Sending file to member TEST in file TEST in library TESTLIB.]To keep the extension .xml, our final URI now looks like this:
ftp://10.1.1.10/%2F/QSYS.LIB/TESTLIB.LIB/test.xml.FILE
where test.xml is the filename and extension of your choice. Trace file result:
System.Net Information: 0 : [3664] FtpControlStream#32854180 - Sending command [STOR test.xml.FILE]
System.Net Information: 0 : [3664] FtpControlStream#32854180 - Received response [150 Sending file to member TEST.XML in file TEST.XML in library TESTLIB.]Note: It seems this syntax with .FILE is only needed if you are uploading to libraries. when uploading to an IFS folder the .FILE should (must?) be left out. That leaves The following URI:
ftp://10.1.1.10/%2F/some/long/path/test.xml
Thanks everyone for your valuable input! Hope this helps someone else.
- Edited by Harmen Brouwer Friday, October 21, 2011 4:04 PM

