Monday, July 9, 2007

Upload file via FTP

. Monday, July 9, 2007

In diversi lavori in Asp.Net 1.1 ho fatto uso di una libreria: EdtFTPNet, per superare l'ostacolo di upload di file tramite FTP. Ma il Framework 2.0 cosa offre?
Sono state introdotte due nuove classi per consentire l'uso del protocollo FTP, cioè FtpWebRequest che deriva da WebRequest e FtpWebResponse che invece deriva da WebResponse.
Il codice che ci consente di eseguire l'upload in un file è veramente molto semplice:

using System.IO;
using System.Net;
try
{
string nameFile = FileUpload.PostedFile.FileName;
//Apro una connessione
FtpWebRequest ftp = (FtpWebRequest)WebRequest.Create( System.IO.Path.Combine( "url ftp", nameFile));
//Aggiungo le credenziali
ftp.Credentials = new NetworkCredential( "username", "password");
//Indico il comando di uplaod
ftp.Method = WebRequestMethods.Ftp.UploadFile;
byte[] arrayFile = new byte[ FileUpload.PostedFile.ContentLength];
Stream read
= FileUpload.PostedFile.InputStream;
//Riempio l'array
read.Read( arrayFile, 0, FileUpload.PostedFile.ContentLength);

Stream w
= ftp.GetRequestStream();
//Scrivo il contenuto dell'array
w.Write(arrayFile, 0, arrayFile.Length);
w.Close();
}
catch (Exception exc)
{
//TODO: log errore
string error = exc.Message;
}


( FileUpload è l'id del nuovo webControl aggiunto in ASP.Net 2.0 che permette di selezionare un file da inviare ad un server )
Da evidenziare la possibilità di indicare tutta una serie di comandi FTP, nel caso specifico abbiamo fatto uso dell'UploadFile ma la scelta è ampia:

  • AppendFile
  • DownloadFile
  • DeleteFile
  • GetDateTimestamp
  • GetFileSize
  • ListDirectoryDetails
  • ListDirectory
  • MakeDirectory
  • PrintWorkingDirectory
  • Rename
  • RemoveDirectory
  • UploadFile
  • UploadFileWithUniqueName

11 commenti:

Anonymous said...

ciao .. ho utilizzato il tuo esempio su un mio sito dal quale vorrei permettere che gli utenti di eseguire upload ma continuo a ricevere questo errore:
System.Net.WebException: Unable to connect to the remote server at System.Net.FtpWebRequest.GetRequestStream() ...
qualche idea? grazie, Davide

Pierluca said...

Ciao Davide,
l'errore mi sembra abbastanza chiaro, non riesci a collegarti al server FTP. Controlla il percorso al server FTP e le credenziali.

Davide said...

Ciao, da browser non ho problemi ad accedere al server ftp e in locale funziona perfettamente, ma quando pubblico su server .. niet!
Ho il sospetto che si sballi il percorso del file, ovvero l'applicazione ricerchi il file da uploaddare sul server stesso e non sul client. Scusami ma sono un pivello in .net, questo è il primo lavoro che faccio e non so proprio dove sbattere la testa.

ti allego il codice casomai riuscissi a darmi una mano:

...
asp:FileUpload ID="uploadFile" runat="server" /
asp:Button ID="btnSend" Text="Upload > >" OnClick="btnSend_OnClick" class="formButton" runat="server" /
...

[codebehind] (leggermente modificato rispetto al tuo, ma la logica mi sembra la medesima)

...
protected void btnSend_OnClick(object sender, EventArgs e)
{
string nameFile = uploadFile.PostedFile.FileName;
string FTPFilePath = Path.Combine("ftp://xxxx.it/", nameFile);


// Create FtpWebRequest object from the Uri provided and Provide the WebPermission Credintials
FtpWebRequest reqFTP = (FtpWebRequest)WebRequest.Create(FTPFilePath);
reqFTP.Credentials = new NetworkCredential("xxx", "xxx");
reqFTP.KeepAlive = true;
reqFTP.UseBinary = true;
reqFTP.ContentLength = uploadFile.PostedFile.ContentLength;

reqFTP.Method = WebRequestMethods.Ftp.UploadFile;

int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;
Stream fs = uploadFile.FileContent;

try
{
// Stream to which the file to be upload is written
Stream strm = reqFTP.GetRequestStream();

// Read from the file stream 2kb at a time
contentLen = fs.Read(buff, 0, buffLength);

// Till Stream content ends
while (contentLen != 0)
{
// Write Content from the file stream to the FTP Upload Stream
strm.Write(buff, 0, contentLen);
contentLen = fs.Read(buff, 0, buffLength);
}

// Close the file stream and the Request Stream
strm.Close();
fs.Close();
}
catch (Exception ex)
{
lblUploadControl.Text = ex.ToString();
}

}
...

grazie intanto e buona giornata

Davide said...

non sono sicuro, specie perchè di server ci capisco poco e nulla, ma è possibile che riceva l'errore perchè web server e ftp server sono sulla stessa macchina? ho provato ad utilizzare dei parametri differenti e funziona tutto (non che mi serva molto, in quanto ho bisogno che i files uploaddati vengano salvati in una cartella specifica del sito, da cui posso poi permettere il download ...).
ciao e buona serata

Pierluca said...

Ciao Davide,
l'ultimo tuo messaggio non mi è chiarissimo (per parametri differenti significa un altro server ecc?).
1)Dici che devi salvare il file in una determinata cartella quindi presumo che il tuo path FTP sia qualcosa del genere (dal tuo codice non sembra):
ftp://server/nomesito/cartellaUtente
2)Hai provato a settare il property UsePassive a false sull'oggetto FTPWebRequest?

Davide said...

Ciao, dunque .. scusa se non sono chiaro .. ma non è nemmeno chiaro a me!
1) per pubblicare online il mio sito, diciamo che l'host sia aruba, utilizzo il seguente indirizzo: ftp://ftp.aruba.it. Se utilizzo questo indirizzo, non riesco ad eseguire l'upload, e mi da l'errore "Unable to connect to the remote server", ma se utilizzo un altro indirizzo (che ho usato per altri siti), per esempio: ftp://ftp2.aruba.it .. allora funziona (ovviamente salvandomi il file sotto quel server).
2) ho provato con UsePassive ma non è cambiato nulla.
3) non c'entra con quanto chiesto prima, ma un altro mio problema è che devo fare upload anche di grandi dimensioni (1gb) .. ho settato il web.config in questo modo:

httpRuntime maxRequestLength="2048000" executionTimeout="3600" shutdownTimeout="3600" requestLengthDiskThreshold="256" /
sessionState timeout="60" /

ma dopo 16mb (più o meno) in remoto e 30mb in locale mi si disconnette dal server. Ora sto tentando la strada di un httpmodules che mi "spacchetti" il post del form in chunk e in modo da spedirli subito via ftp sul server. Non sono sicuro, ma mi pare che, a prescindere dalle configurazioni, la richista post non arrivi nemmeno al codice dell'upload in caso di grosse dimensioni: della serie, prima si legge tutto il file, poi procede all'upload. e questo credo sia il motivo del timeout che mi ricevo... ti è capitato di lavorare su questo problema? consigli?
Grazie ancora per le risposte,
Buona giornata,
Davide

Pierluca said...

Ciao Davide,
1)a questo punto credo che la cosa più ovvia da fare sia quella di chiedere spiegazione (sull'indirizzo e credenziali) ad Aruba.
3)sinceramente non ho mai dovuto implementare upload per file di grosse dimensioni (in generale il limite dei 4Mb era più che sufficiente). Spero che comunque i server di Aruba non presentano dei limiti di tempo/dimensione file.
Detto questo personalmente non userei per un "compito del genere" il controllo FileUpload. Di controlli più evoluti (che mostrano per esempio l'avanzamento all'utente, cosa che dovresti implementare te) c'è ne sono.
Per esempio puoi fare delle prove con: SWFUpload(http://swfupload.org/project).
Qui puoi anche scaricare un esempio per Asp.Net 2.0:
http://swfupload.org/node/47
Poi mi dici come sono andate le prove ;)

Davide said...

Ciao Pierluca,
direi di aver quasi risolto i miei problemi, per l'upload di grosse dimensioni ho "dovuto" usare una terza parte (http://darrenjohnstone.net/2008/07/15/aspnet-file-upload-module-version-2-beta-1/) in quanto sono troppo ignorante per riuscire a scrivere tutto da solo (per ora almeno ;)

il problema, lo dico per eventuali visitatori futuri interessati, è questo, almeno per come l'ho capito io:

se devo fare un upload da 1gb, prima ancora che possa intervenirci sopra e modificarlo occorre che arrivi al server il post dal form e in questo intervallo si va in timeout, a prescidere dalle impostazioni del web.config.

Soluzione: agire a livello iniziale della pipeline con un http module che "spacchetti" la richiesta e da li iniziare l'upload. A parole è semplice da fare io non ci riesco ;) Comunque il link indicato è ricco di spunti, il codice è ben commentato e semplice da implementare .. insomma, un buon inizio!

Grazie di tutto, alla prossima
Davide

Nick said...

Ciao Pierluca, stavo provando il tuo codice ma subito dovuto fermarmi in quanto mi da questo errore di conversione "Impossibile eseguire il cast di oggetti di tipo 'System.Net.FileWebRequest' sul tipo 'System.Net.FtpWebRequest'" sulla riga

Dim ftp As FtpWebRequest = CType(WebRequest.Create(Path.Combine("ftp://ftp.xxx.it/", nameFile)), FtpWebRequest)

che è la riga di codice del tuo esempio in VB...
Hai qualche idea ?

Grazie e buona giornata

Nick said...

Risolto...
Non leggeva correttamente il nome file, ma ho risolto facendo
Path.GetFileName(FileUpload.PostedFile.FileName)

Ciao e complimenti

Pierluca said...

Bene ;) e grazie per i complimenti.

Post a Comment