FtpWebRequest实战总结

视觉项目在同时产生批量图片后,客户要求这些图片存底和追溯。以前提供的网盘存图无法满足最新现场的要求,需要增加ftp上传模式。


先看上传的核心代码:

                List str = new List();
                str.Add(path);
                str.Add(DateTime.Now.ToString("yyyyMMdd"));
                if (!string.IsNullOrWhiteSpace(station)) { str.Add(station); }
                if (!string.IsNullOrWhiteSpace(info)) { str.Add(info); }

                path = path + "\\" + DateTime.Now.ToString("yyyyMMdd") + "\\" + station;
                string pathAfter = path + "\\" + info;

                FtpWebRequest reqFTP;
                pathAfter = pathAfter.Replace('\\', '/');
                var ftppath = str[0];
                if (str != null && str.Count > 0)
                {
                    for (int i = 0; i < str.Count; i++)
                    {
                        if (!string.IsNullOrWhiteSpace(str[i]))
                        {
                            if (!DirectoryExists(ftppath, userName, pwd))
                            {
                                reqFTP = WebRequest.Create(new Uri(ftppath)) as FtpWebRequest;
                                reqFTP.Credentials = new NetworkCredential(userName, pwd);
                                reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
                                reqFTP.UseBinary = true;
                                reqFTP.UsePassive = true;
                               
                                FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
                                response.Close();
                                reqFTP.Abort();
                            }
                            if (i + 1 < str.Count)
                            {
                                ftppath = ftppath + '/' + str[i + 1];
                            }

                        }
                    }
                }
                string strImageName = pathAfter + "\\" + imageName + "_" + DateTime.Now.ToString("HH_mm_ss_ffff") + strImageTypes[(int)imageType];
                byte[] buffer = imageBuffer;
                ...
                FtpWebRequest request;
                strImageName = strImageName.Replace('\\', '/');
                request = WebRequest.Create(new Uri(strImageName)) as FtpWebRequest;
                request.Method = WebRequestMethods.Ftp.UploadFile;
                request.UseBinary = true;
                request.UsePassive = true;
                request.Credentials = new NetworkCredential(userName, pwd);

                using (MemoryStream ms = new MemoryStream())
                {
                    Stream requestStream = request.GetRequestStream();
                    requestStream.Write(buffer, 0, buffer.Length);
                    requestStream.Flush();
                    requestStream.Close();
                }
         
                

上传图片前需验证当前路径,如果不存在,要创建路径后再写入流文件。验证路径有两种方式:

  1. WebRequestMethods.Ftp.ListDirectory类显示路径清单后和当前路径做比较。

  1. 先用WebRequestMethods.Ftp.UploadFile上传测试文件,再WebRequestMethods.Ftp.DeleteFile删除文件。整个操作具有原子性,失败即不存在路径。

我使用的第二种方式,本地测试过程是没有问题的。但没有考虑到现场复杂性,整个流程单次进行能正常运行,流程一个接一个时,这里就报错,导致图片上传老是漏几张。

错误提示:"系统错误"。

Ftp通过网络传输,用流文件的方式上传,特别在验证路径时,通过拆分每一级目录,对每级目录上传和删除的操作,增加网络和IO的消耗,操作FTP服务器频率高导致的偶尔失败。

解决方案:

  1. 重试机制

第一次读写FTP失败后,在catch里再次操作。最简单并且能马上解决问题的办法是封装上方循环目录的部分代码,在try和catch里分别调用一次,可减少该问题发生的概率。

  1. 异步

msdn代码地址

详情如下:

using System;
using System.Net;
using System.Threading;

using System.IO;
namespace Examples.System.Net
{
    public class FtpState
    {
        private ManualResetEvent wait;
        private FtpWebRequest request;
        private string fileName;
        private Exception operationException = null;
        string status;

        public FtpState()
        {
            wait = new ManualResetEvent(false);
        }

        public ManualResetEvent OperationComplete
        {
            get {return wait;}
        }

        public FtpWebRequest Request
        {
            get {return request;}
            set {request = value;}
        }

        public string FileName
        {
            get {return fileName;}
            set {fileName = value;}
        }
        public Exception OperationException
        {
            get {return operationException;}
            set {operationException = value;}
        }
        public string StatusDescription
        {
            get {return status;}
            set {status = value;}
        }
    }
    public class AsynchronousFtpUpLoader
    {
        // Command line arguments are two strings:
        // 1. The url that is the name of the file being uploaded to the server.
        // 2. The name of the file on the local machine.
        //
        public static void Main(string[] args)
        {
            // Create a Uri instance with the specified URI string.
            // If the URI is not correctly formed, the Uri constructor
            // will throw an exception.
            ManualResetEvent waitObject;

            Uri target = new Uri (args[0]);
            string fileName = args[1];
            FtpState state = new FtpState();
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(target);
            request.Method = WebRequestMethods.Ftp.UploadFile;

            // This example uses anonymous logon.
            // The request is anonymous by default; the credential does not have to be specified.
            // The example specifies the credential only to
            // control how actions are logged on the server.

            request.Credentials = new NetworkCredential ("anonymous","[email protected]");

            // Store the request in the object that we pass into the
            // asynchronous operations.
            state.Request = request;
            state.FileName = fileName;

            // Get the event to wait on.
            waitObject = state.OperationComplete;

            // Asynchronously get the stream for the file contents.
            request.BeginGetRequestStream(
                new AsyncCallback (EndGetStreamCallback),
                state
            );

            // Block the current thread until all operations are complete.
            waitObject.WaitOne();

            // The operations either completed or threw an exception.
            if (state.OperationException != null)
            {
                throw state.OperationException;
            }
            else
            {
                Console.WriteLine("The operation completed - {0}", state.StatusDescription);
            }
        }
        private static void EndGetStreamCallback(IAsyncResult ar)
        {
            FtpState state = (FtpState) ar.AsyncState;

            Stream requestStream = null;
            // End the asynchronous call to get the request stream.
            try
            {
                requestStream = state.Request.EndGetRequestStream(ar);
                // Copy the file contents to the request stream.
                const int bufferLength = 2048;
                byte[] buffer = new byte[bufferLength];
                int count = 0;
                int readBytes = 0;
                FileStream stream = File.OpenRead(state.FileName);
                do
                {
                    readBytes = stream.Read(buffer, 0, bufferLength);
                    requestStream.Write(buffer, 0, readBytes);
                    count += readBytes;
                }
                while (readBytes != 0);
                Console.WriteLine ("Writing {0} bytes to the stream.", count);
                // IMPORTANT: Close the request stream before sending the request.
                requestStream.Close();
                // Asynchronously get the response to the upload request.
                state.Request.BeginGetResponse(
                    new AsyncCallback (EndGetResponseCallback),
                    state
                );
            }
            // Return exceptions to the main application thread.
            catch (Exception e)
            {
                Console.WriteLine("Could not get the request stream.");
                state.OperationException = e;
                state.OperationComplete.Set();
                return;
            }
        }

        // The EndGetResponseCallback method
        // completes a call to BeginGetResponse.
        private static void EndGetResponseCallback(IAsyncResult ar)
        {
            FtpState state = (FtpState) ar.AsyncState;
            FtpWebResponse response = null;
            try
            {
                response = (FtpWebResponse) state.Request.EndGetResponse(ar);
                response.Close();
                state.StatusDescription = response.StatusDescription;
                // Signal the main application thread that
                // the operation is complete.
                state.OperationComplete.Set();
            }
            // Return exceptions to the main application thread.
            catch (Exception e)
            {
                Console.WriteLine ("Error getting response.");
                state.OperationException = e;
                state.OperationComplete.Set();
            }
        }
    }
}

总结如下:

  1. 先研究技术,在msdn官网查询Ftp有关类的操作。官方其实不建议FtpWebRequest用于新开发,更建议用HttpClient。

  1. 充分考虑场景,本地测试没有问题,现场使用时总有意想不到的bug出现。下次在做一个新的功能时,需要模拟现场环境后,测试通过才是第一步。

你可能感兴趣的:(视觉检测,c#,视觉检测,后端)