2013-07-03 21 views
8

Tôi đang cố gắng tạo một phương thức đơn giản để tải xuống tệp từ FTP bằng cách sử dụng FtpWebRequest với phương thức WebRequestMethods.Ftp.DownloadFile. Vấn đề là tôi không thể hiển thị tiến độ tải xuống và do đó cần phải biết kích thước tệp trước để có thể tính toán tỷ lệ phần trăm được chuyển. Nhưng khi tôi gọi GetResponse trong số FtpWebRequest thành viên ContentLength là -1.Sử dụng lại FtpWebRequest

OK - vì vậy tôi nhận được kích thước của tệp trước bằng cách sử dụng phương thức WebRequestMethods.Ftp.GetFileSize. Không vấn đề gì. Sau đó, sau khi nhận được kích thước tôi tải về tập tin.

Đây là nơi các vấn đề trong câu hỏi xuất hiện ...

Sau khi nhận được kích thước tôi cố gắng tái sử dụng các FtpWebRequest và reset phương pháp để WebRequestMethods.Ftp.DownloadFile. Điều này gây ra một số System.InvalidOperationException nói điều gì đó như "Không thể thực hiện tác vụ này sau khi gửi yêu cầu". (có thể không phải là công thức chính xác - được dịch từ từ tôi nhận được bằng tiếng Thụy Điển).

Tôi đã tìm thấy ở nơi khác miễn là tôi đặt thuộc tính KeepAlive thành true, không quan trọng, kết nối vẫn được duy trì hoạt động. Đây là những gì tôi không hiểu ... Đối tượng duy nhất tôi tạo ra là đối tượng FtpWebRequest của tôi. Và nếu tôi tạo một cái khác, làm thế nào nó có thể biết được sử dụng kết nối nào? Và thông tin nào?

Pseudo code:

Create FtpWebRequest 
Set Method property to GetFileSize 
Set KeepAlive property to true 
Set Credentials property to new NetworkCredential(...) 
Get FtpWebResponse from the request 
Read and store ContentLength 

Bây giờ tôi có kích thước tập tin. Vì vậy, đã đến lúc tải xuống tệp. Phương thức thiết lập biết gây ra ngoại lệ được đề cập ở trên. Vậy tôi có tạo FtpWebRequest mới không? Hoặc là có anyway để đặt lại yêu cầu để được tái sử dụng? (Đóng phản hồi không có sự khác biệt.)

Tôi không hiểu cách di chuyển về phía trước mà không cần phải tạo lại đối tượng. Tôi có thể làm điều đó, nhưng nó không cảm thấy đúng. Vì vậy, tôi đăng bài ở đây với hy vọng tìm được cách làm đúng.

Đây là (không làm việc) mã (Đầu vào là Suri, sDiskName, Suser và sPwd.):

FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(sURI); 
request.Method = WebRequestMethods.Ftp.GetFileSize; 
request.Credentials = new NetworkCredential(sUser, sPwd); 
request.UseBinary = true; 
request.UsePassive = true; 
request.KeepAlive = true; 

FtpWebResponse resp = (FtpWebResponse)request.GetResponse(); 
int contLen = (int)resp.ContentLength; 
resp.Close(); 

request.Method = WebRequestMethods.Ftp.DownloadFile; 

resp = (FtpWebResponse)request.GetResponse(); 

Stream inStr = resp.GetResponseStream(); 
byte[] buff = new byte[16384]; 

sDiskName = Environment.ExpandEnvironmentVariables(sDiskName); 
FileStream file = File.Create(sDiskName); 

int readBytesCount; 
int readTotal=0; 

while ((readBytesCount = inStr.Read(buff, 0, buff.Length)) > 0) 
{ 
    readTotal += readBytesCount; 
    toolStripProgressBar1.Value = 100*readTotal/contLen; 
    Application.DoEvents(); 
    file.Write(buff, 0, readBytesCount); 
} 
file.Close(); 

Tôi hy vọng ai đó có thể giải thích cách này là nghĩa vụ phải làm việc. Cảm ơn trước.

+0

Ít nhất với HTTPWebRequests - bạn luôn phải tạo một tài khoản mới. Giả sử cùng một vấn đề ở đây. – Hikiko

+0

'FtpWebRequest' rất phù hợp để thực hiện các yêu cầu duy nhất. Bạn muốn một máy khách FTP, kết nối và duy trì kết nối liên tục cho đến khi bạn đóng nó. 'System.Net.FtpClient' có vẻ được coi là tốt: http://netftp.codeplex.com/ –

Trả lời

6

Tôi không nghĩ rằng điều này sẽ được trả lời vì vậy tôi "đóng nó" bằng cách cho bạn biết cách tôi giải quyết nó.

Vâng, tôi đã không thực sự giải quyết nó. Tuy nhiên, tôi đã thử tải xuống bằng cách tạo lại FtpWebRequest và nhận thấy rằng trên máy chủ FTP, nó hoạt động như tôi muốn, tức là chỉ một lần đăng nhập và sau đó thực thi tuần tự các yêu cầu của tôi.

Đây là cách mã nhận được kích thước tập tin và bắt đầu tải về kết thúc:

// Start by fetching the file size 
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(sURI); 

request.Method = WebRequestMethods.Ftp.GetFileSize; 
NetworkCredential nc = new NetworkCredential(sUser, sPwd); 
request.Credentials = nc; 
request.UseBinary = true; 
request.UsePassive = true; 
request.KeepAlive = true; 

// Get the result (size) 
FtpWebResponse resp = (FtpWebResponse)request.GetResponse(); 
Int64 contLen = resp.ContentLength; 

// and now download the file 
request = (FtpWebRequest)FtpWebRequest.Create(sURI); 
request.Method = WebRequestMethods.Ftp.DownloadFile; 
request.Credentials = nc; 
request.UseBinary = true; 
request.UsePassive = true; 
request.KeepAlive = true; 

resp = (FtpWebResponse)request.GetResponse(); 

Vì vậy, không trả lời trên nếu nó có thể thiết lập lại các FtpWebRequest để tái sử dụng. Nhưng ít nhất tôi biết không có thông tin dư thừa được chuyển giao.

Nhờ tất cả mọi người đã quan tâm và dành thời gian nghĩ đến câu trả lời.

+0

Câu trả lời này http://stackoverflow.com/a/7132428/2170171 làm sáng tỏ một số cách làm việc đó, vì vậy về cơ bản bạn đang làm điều 'đúng'. Và điều này http://stackoverflow.com/a/9666598/2170171 cho thấy làm thế nào để theo dõi yêu cầu FTP, vì vậy bạn có thể thấy rằng kết nối thực sự được sử dụng lại giữa các yêu cầu – Lanorkin

0

Bạn có thể muốn sử dụng phương pháp Async. Đây là liên kết đến tài liệu MSDN.

http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.aspx

GetResponseAsync() 

Điều đó sẽ giữ cho ứng dụng của bạn từ khóa lên quá, vì vậy bạn sẽ không phải sử dụng

Application.DoEvents(); 

Bạn cũng có thể nhìn vào có thể sử dụng một thư viện ftp thay thế. imo FtpWebRequest không chính xác là lớp ftp tốt nhất. Tìm kiếm nhanh đã bật thư viện này. Ftp không phải là không trạng thái như HTTP. Tôi thích các thư viện cho phép bạn tạo một ứng dụng khách, mở một kết nối và giữ cho kết nối luôn hoạt động.

http://sanity-free.org/dist/NullFX.Net-binary.zip

Đây là exacmple mã tôi tìm thấy

FtpClient client = 
    new FtpClient( 
     new IPEndPoint(IPAddress.Loopback, 21), 
     new NetworkCredential("test", "[email protected]") 
    ); 
client.Connect(); 
client.Download("testfile.zip", @"C:\downloads\testfile.zip"); 

Nguồn là có quá, vì vậy bạn sẽ có thể để có thể đính kèm một số sự kiện để quá trình đọc để theo dõi tiến trình download.

+0

Trong trường hợp này, tôi hơi miễn cưỡng khi sử dụng các sản phẩm của bên thứ ba. Và tôi cũng nhận thức được các hoạt động bên trong của FTP như tôi đã viết cả hai máy chủ và thư viện máy khách từ đầu. Những gì tôi sau đây là để biết nếu nó có thể làm với 'FtpWebRequest'. Nếu không, tôi đoán tôi sẽ đi với cách tiếp cận Asynch (mà tôi đã chọn ra trước đó vì sự phức tạp thêm vào mã). Bit của một sự xấu hổ mặc dù làm phức tạp nó như thế, khi tôi không thực sự sau khi hành vi không đồng bộ, ngoài việc cập nhật thanh tiến trình ... Cảm ơn anyway. – ClasG

3

FtpWebRequest chỉ có thể được sử dụng cho 1 yêu cầu, như nhận kích thước tệp hoặc tải tệp xuống, chứ không phải cả hai. Bạn phải tạo 2 FtpWebRequests. Đằng sau hiện trường, FtpWebRequest thông báo rằng nó là cùng một URL và thông tin xác thực và sẽ tái sử dụng cùng một kết nối ftp mà không đóng nó, miễn là IsKeepAlieve là đúng, đó là thiết lập mặc định.

Đây là một ví dụ đáng buồn về thiết kế tồi của Microsoft. Thay vì cho phép chúng tôi mở và đóng kết nối một cách rõ ràng, họ muốn tự động làm điều đó cho chúng tôi và gây nhầm lẫn cho mọi người.

+0

Chỉ cần làm rõ, là không thể với FtpWebRequest để gửi nói 10 tập tin KHÔNG đăng nhập 10 lần? –

Các vấn đề liên quan