问题
I've an application written in C# (.Net 3.5) with this code.
using System;
using System.Net;
string strURI = String.Format("ftp://x{0}ftp/%2F'{1}'", parm1, parm2);
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(strURI);
ftp.Proxy = null;
ftp.KeepAlive = true;
ftp.UsePassive = false;
ftp.UseBinary = false;
ftp.Credentials = new NetworkCredential("uid", "pass");
ftp.Method = WebRequestMethods.Ftp.DownloadFile;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
...
I've translated this into F# (.Net 4.0) to use in my application.
open System.Net
let uri = sprintf "ftp://x%sftp/%%2F'%s'" parm1 parm2
let ftp = FtpWebRequest.Create(uri) :?> FtpWebRequest
ftp.Credentials <- new NetworkCredential("uid", "pass")
ftp.Method <- WebRequestMethods.Ftp.DownloadFile
ftp.Proxy <- null
let response = ftp.GetResponse() :?> FtpWebResponse
...
At this point, FSI complains.
System.Net.WebException: The remote server returned an error: (550) File unavailable (e.g. file not found, no access).
Yet the C# application runs and successfully downloads the file. What am I missing in F# (besides the properties that aren't there in 4.0, i.e. KeepAlive, UsePassive, and UseBinary)?
回答1:
The code may have errors, I don't have F# compiler right now.
open System
open System.Reflection
open System.Net
let switch_to_legacy_mode _ =
let wtype = typeof<FtpWebRequest>
let mfield = wtype.GetField("m_MethodInfo", BindingFlags.NonPublic ||| BindingFlags.Instance)
let mtype = mfield.FieldType
let knfield = mtype.GetField("KnownMethodInfo", BindingFlags.Static ||| BindingFlags.NonPublic)
let knarr = knfield.GetValue(null) :?> Array
let flags = mtype.GetField("Flags", BindingFlags.NonPublic ||| BindingFlags.Instance)
let flag_val = 0x100
for f in knarr do
let mutable ff = flags.GetValue(f) :?> int
ff <- ff ||| flag_val
flags.SetValue(f, ff)
let uri = sprintf "ftp://x%sftp/%%2F'%s'" parm1 parm2
do switch_to_legacy_mode () // Call it once before making first FTP request
let ftp = FtpWebRequest.Create(uri) :?> FtpWebRequest
ftp.Credentials <- new NetworkCredential("uid", "pass")
ftp.Method <- WebRequestMethods.Ftp.DownloadFile
ftp.Proxy <- null
let response = ftp.GetResponse() :?> FtpWebResponse
Source: http://support.microsoft.com/kb/2134299
The cause of this issue is due to a behavior change in the System.Net.FtpWebRequest class in .Net Framework 4. There has been a change made to the System.Net.FtpWebRequest class from .Net Framework 3.5 to .Net Framework 4 to streamline the use of the CWD protocol commands. The new implementation of the System.Net.FtpWebRequest class prevents the send of extra CWD commands before issuing the actual command which the user requested and instead directly sends the requested command. For fully RFC compliant FTP servers, this should not be an issue, however for non-fully RFC compliant servers, you will see these types of errors.
回答2:
Try this instead:
open System
open System.Net
let parm1 = "test"
let parm2 = "othertest"
let uri = String.Format("ftp://x{0}ftp/%2F'{1}'", parm1, parm2)
let ftp = FtpWebRequest.Create(uri) :?> FtpWebRequest;;
I had to add a dummy parm1 and parm2 but I'm assuming you've got parm1 and parm2 defined in your actual code. I'd guess that whatever the issue is, it lies in the uri string. That is, I think you'll find the problem by comparing uri to the known to be good uri string.
来源:https://stackoverflow.com/questions/15644969/f-ftp-fails-where-c-sharp-succeeds