Newsgroup: it.comp.lang.visual-basic
Titolo: Collegamento FTP

Sto facendo un lavoretto di collegamento in ftp tra due computer.
Dovrei trasferire file anche abbastanza lunghi e mi servirebbe, man mano che
il file viene trasmesso, sapere la percentuale di file trasmesso, altrimenti
sembra che non funzioni.
Uso wininet.dll per il collegamento, ma non sono riuscito a capire se c'è
una routine che mi dà questa funzione.
Qualcuno può aiutarmi???
Grazie
Sergio

 il file viene trasmesso, sapere la percentuale di file trasmesso,
altrimenti
Vai su altavista, e cerca:
   +bradley +xftp
troverai una pagina dalla quale puoi prelevare xftp, ottime librerie per
l'uso di ftp. gratuite.
-vibino

 una routine che mi dà questa funzione.
Ne abbiamo parlato giorni fa sul NG.
Ci sono le funzioni InternetReadFile() e InternetWriteFile()
per la lettura/scrittura di dati con protocollo ftp.
"Chi conosce l'API "DoFileDownload" del 22/09
"Aiutino per usare l'API InternetWriteFile" del 27/09
Ci sono le funzioni praticamente già fatte.
Dato che poi il discorso è continuato in privato, mancano forse
alcuni pezzi. Se ti serve aiuto, riscrivi.


Grazie Jimi
Ho cercato sul gruppo, ma i messaggi non erano più disponibili sul server.
Non ho capito bene se usano la libreria "wininet.dll", ma comunque vorrei
provare queste funzioni.
Puoi aiutarmi?
Grazie
Sergio

 provare queste funzioni.
--- Spiacente del ritardo. Troppi impegni.
--- Spero che questo sia sufficientemente completo
----------------------------------------------------------------------
Nota: Tutte le funzioni e i metodi che elenco in seguito hanno lo scopo
di ottenere connessioni non-blocking.
Dato il modo (asincrono) di procedere, č utile tenere presente che
č possibile istanziare pił sessioni contemporanee opportunamente
concorrenziali. Lo scopo esplicito č di bypassare nella maniera pił
"elegante" possibile VB, date le "difficoltą" che si incontrano nel gestire
il multithreading da questo ambiente.
----------------------------------------------------------------------
Per iniziare una sessione FTP, occorre innanzi tutto inizializzare
il processo. Si usa allo scopo la funzione InternetOpen(), la quale
rende un handle di sessione, che viene in seguito utilizzato dalle
funzioni che si occupano della connessione.
Per iniziare una connessione con un server FTP si usa la funzione
InternetConnect(), che ha lo scopo fondamentale di autenticare
la richiesta di connessione (come InternetOpen() ha la funzione di
identificare lo UserAgent).
Da tenere presente che la funzione InternetOpen() inizializza solamente
l'interfaccia; la connessione viene effettuata da InternetConnect(). Se
il collegamento ad Internet non č attivo, la funzione blocca l'applicazione
per un periodo di timeout stabilito. Per evitare questo blocco occorre
utilizzare una Callback, il cui "ambito di competenza" č specificato dal
parametro dwContext, in relazione all'handle corrente.
----------------------------------------------------------------------
Nota: Occorre una precisazione sull'uso degli handle.
L'handle (HINTENET) ritornato da InternetOpen() č da considerarsi
globale per la sessione corrente (global session handle). Puņ essere
utilizzato per tutte le operazioni che seguono. Questo non vale per
la funzione InternetConnect(). L'handle di connessione č valido solo
nell'ambito (scope) dei criteri di creazione della connessione. Se viene
creata una connessione di tipo FTP, questo handle potrą essere utilizzato
solo per operazioni di questo tipo, non per altre. Lo stesso vale per la
funzione FtpFindFirstFile(), la quale restituisce un handle di tipo FIND,
utile solo per il processo di listing (enumeration) e FtpOpenFile() che
restituisce un handle di tipo FILE.
Per chi ha la guida di Platform SDK 2000, questi handle sono elencati
in base al suffisso INTERNET_HANDLE_TYPE_*
----------------------------------------------------------------------
Ottenuta la connessione, č possibile richiedere un'operazione di
listing della struttura della directory corrente o di download/upload
di un file.
Per iniziare un'operazione di listing, si possono utilizzare le funzioni
InternetOpenUrl() o FtpFindFirstFile(). Da notare che InternetOpenUrl()
richiede un handle di sessione, mentre FtpFindFirstFile() richiede un
handle di connessione (ossia č necessario aver ottenuto in precedenza
un handle di connessione tramite InternetConnect()).
Dato che InternetOpenUrl() puņ operare in assenza di connessione
esplicita, consiglio di utilizzare FtpFindFirstFile() per le normali
operazioni di listing in connessioni di tipo FTP, mentre InternetOpenUrl()
puņ essere molto comoda se si richiede un listing ad un server FTP via
HTTP. In questo caso la risposta sarą una pagina html pre-formattata
(un po' come succede se si accede ad un server ftp tramite Internet Explorer).
La connessione esplicita tramite InternetConnect() dovrebbe garantire che
il server non rifiuti la richiesta per "carenze" di attributi di connessione.
Questo perché InternetConnect() stabilisce una connessione autenticata,
dato che la funzione richiede di specificare Username e Password (anche
se č possibile comunque usare i default tipo "anonymous" e "guest@unknown" ).
Volendo comunque utilizzare InternetOpenUrl() per il listing, č necessario
che il parametro dwFlags sia INTERNET_FLAG_RAW_DATA
(anche se nessuno vieta di effettuare il parsing della pagina html ottenuta
altrimenti).
FtpFindFirstFile() restituisce in una struttura WIN32_FIND_DATA
il nome del primo file o della prima directory in elenco e un handle
che permette di continuare il listing (enumeration), passandolo alla
funzione InternetFindNextFile(). Il discriminante tra una risorsa di tipo
file o di tipo directory č il membro dwFileAttributes della struttura.
Il processo di listing in genere viene concluso quando l'handle ritornato
č nullo e l'errore č ERROR_NO_MORE_FILES.
Una volta conclusa l'operazione di listing, č assolutamente necessario
chiudere l'handle utilizzato. Si usa la funzione InternetCloseHandle().
----------------------------------------------------------------------
Nota: I nomi di file e directory arrivano in ordine sparso. Inoltre
i dati relativi al periodo di creazione, modifica e accesso possono
non essere coerenti. La lunghezza per fortuna di solito č giusta.
Il nome del file nel formato 8+3 non č sempre disponibile.
----------------------------------------------------------------------
Questo tipo di procedimento č piuttosto comodo, perché assomiglia
molto al modo in cui normalmente si opera con i file locali usando
le funzioni del kernel. Se si č abituati ad usare il kernel per le operazioni
su file, la procedura risulta senz'altro banale.
Se per iniziare un'operazione di lettura/scrittura su un file si usa la
funzione OpenFile() del kernel, in una sessione ftp si usa la funzione
FtpOpenFile(). La modalitą operativa č la stessa.
Si definisce il tipo di operazione da compiere impostando il parametro
dwAccess a GENERIC_READ oppure GENERIC_WRITE.
Il parametro dwFlags č bene che sia esplicitamente impostato a
FTP_TRANSFER_TYPE_BINARY, anche se questo č il default.
Inoltre č bene specificare anche INTERNET_FLAG_RELOAD,
per evitare che vengano caricate le referenze dalla cache.
FtpOpenFile() restituisce un file operation session handle che puņ
essere usato con le funzioni InternetReadFile() o InternetWriteFile(),
in base all'operazione che si sta effettuando.
--------------------------------------------------------------
Nota: Le funzioni InternetConnect() e FtpFindFirstFile() (oltre che
FtpCommand(), della quale perņ non parlo) consentono di utilizzare
una Callback per la valutazione dello stato della connessione.
L'utilizzo di una Callback puņ risultare necessario se si desidera
controllare il processo di connessione ed eventualmente terminarlo
se supera un determinato periodo di latenza. Occorre anche
considerare che il processo non risulta, nel complesso, non-blocking
se non vengono sfruttate queste funzioni.
--------------------------------------------------------------
Schema riassuntivo --> FtpFindFirstFile() e FtpOpenFile()
| -- Open Session                         --> InternetOpen()
|     | -- Open Connection               --> InternetConnect()
|     |     | -- Start Enum                   --> FtpFindFirstFile()
|     |     |     | -- Continue Enum       --> InternetFindNextFile()
|     |     |           | -- End Enum         --> InternetCloseHandle()
|     |     |
|     |     | -- File Op. Session            --> FtpOpenFile()
|     |           | -- Read File                --> InternetReadFile()
|     |           | -- Write File                --> InternetWriteFile()
|     |                 || -- End File Op.     --> InternetCloseHandle()
|     |
|     | -- Close Connection                --> InternetCloseHandle()
| -- Close Session                            --> InternetCloseHandle()
--------------------------------------------------------------
Schema riassuntivo --> InternetOpenUrl()
| -- Open Session                           --> InternetOpen()
|     | -- Start Enum                          --> InternetOpenUrl() *
|     |     | -- Continue Enum              --> InternetFindNextFile()
|     |           | -- End Enum               --> InternetCloseHandle()
|     |
|     | -- File Op. Session                  --> InternetOpenUrl()
|           | -- Read File                       --> InternetReadFile()
|                 | -- End File Op.            --> InternetCloseHandle()
|
| -- Close Session                            --> InternetCloseHandle()
* InternetOpenUrl() consente solo operazioni di lettura.
--------------------------------------------------------------
--------------------------------------------------------------
Pseudo-codice di esempio per il download/upload di un file.
--------------------------------------------------------------
Dim hInternet As Long
Dim hConnection As Long
Dim hFind As Long
Dim hFile As Long
Dim FindData As WIN32_FIND_DATA
  ' La prima operazione č l'inizializzazione dell'interfaccia
  hInternet = InternetOpen([UserAgent], _
                    INTERNET_OPEN_TYPE_PRECONFIG, _
                    vbNullString, _
                    vbNullString, _
                    &H0)
   ' [UserAgent] puņ essere una qualunque stringa che identifica
   ' il software che accede al servizio, tipo "My FTP Client".
  If hInternet = 0 --> Inizializzazione fallita
        |- InternetCloseHandle(hInternet)
  Else                  --> Inizializzazione completata
  ' Se l'operazione fallisce, puņ essere utile ritentare specificando
  ' INTERNET_OPEN_TYPE_DIRECT prima di considerare
  ' terminato il processo.
  ' Si tenta quindi la connessione
    hConnection = InternetConnect(hInternet, _
                           [ServerName], _
                           INTERNET_INVALID_PORT_NUMBER, _
                           [UserName], _
                           [Password], _
                           INTERNET_SERVICE_FTP, _
                           FTP_TRANSFER_TYPE_BINARY, _
                            &H0)
   ' Specificando INTERNET_INVALID_PORT_NUMBER si
   ' indica di usare la porta di default per il servizio a cui ci si connette.
   ' In questo caso la porta 21. E' possibile indicare una qualsiasi altra porta.
  If hConnection = 0 --> Connessione fallita
        |- InternetCloseHandle(hConnection)
  Else                        --> Connessione completata
  ' Elenco dei file nella directory corrente
   hFind = FtpFindFirstFile(hConnection, _
                "*.*", _
                FindData, _
                &H0, _
                &H0)
   If hFind = 0 Then
        -- Se Err.LastDllError = ERROR_NO_MORE_FILES
            -->  La directory non contiene file
                |- InternetCloseHandle(hFind)
        Else
            -- Si č verificato un errore
                |- InternetCloseHandle(hFind)
        End If
  End If
  ' Si procede con il listing dei file
  ' Il primo file/directory in elenco č contenuto nella struttura passata
  [FileName] = FindData.cFileName
  ' Il membro .ftLastWriteTime contiene il FileTime dell'ultima
  ' scrittura. E' in formato Win32, quindi deve essere convertito.
  ' Si usa allo scopo la funzione FileTimeToLocalFileTime()
  ' Si recuperano gli altri
  Do
        FindData.cFileName = Space(MAX_PATH)
        [Risposta] = InternetFindNextFile(hFind, FindData)
        If [Risposta] = False Then
             -- Se Err.LastDllError = ERROR_NO_MORE_FILES
             -->  Il listing č completato
                   Exit Do
             Else
                  -- Si č verificato un errore
                   |- InternetCloseHandle(hFind)
                   |- End
        Else
             [NewFileName] = FindData.cFileName
             [NewFileTime] = FindData.ftLastWriteTime
             If FindData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY Then
                 ' E' una directory
             Else
                 ' E' un file
                 [NewFileSize] = (FindData.nFileSizeHigh *  MAXDWORD) _
                                           + FindData.nFileSizeLow
             End If
       End If
   Loop
   InternetCloseHandle (hFind)
   ' Si recupera alla fine il nome della directory corrente, sia per verifica
   ' sia per evitare errori dovuti a redirection
   [Buffer] = String(MAX_PATH, Chr$(0))
   [Risposta] = FtpGetCurrentDirectory(hConnection, _
                       [Buffer], _
                       MAX_PATH)
   If [Risposta] = False Then
       ' Errore
   Else
       [CurrentDirectory] = Left$([Buffer], (Instr([Buffer], Chr$(0)) - 1)
   End If
   ' Download di un file
   ' Si richiede un handle per il file da scaricare
    ' Non viene specificata una Callback --> modalitą blocking
    hFile = FtpOpenFile(hConnection, _
                [FileName], _
                GENERIC_READ, _
                FTP_TRANSFER_TYPE_BINARY Or _
                INTERNET_FLAG_RELOAD, _
                &H0)
    If hFile = 0 Then
        ' Errore
    Else
        ' Si procede con la lettura del file.
        ' Occorre allocare un buffer per contenere i dati in arrivo
        [BufferLen] = 512
        [Buffer] = String([BufferLen], Chr$(0))
        Open [FileName] For Binary Access Write As #[FileNum]
        Do
             ' La variabile [BytesRead] riporta il numero di byte che sono
             ' stati effettivamente letti. Puņ essere differente da [BufferLen].
             [Risposta] = InternetReadFile(hFile, _
                                [Buffer], _
                                [BufferLen], _
                                [BytesRead]
              If [Risposta] = True Then
                  If [BytesRead] > 0 Then
                      ' Sono stati letti dei byte
                      Put #[FileNum], , Left$([Buffer], [BytesRead])
                  Else
                       ' La lettura č terminata. Almeno in teoria. Occorre chiaramente
                       ' verificare che il numero di byte scaricati sia uguale alla
                       ' dimensione del file che si č richiesto
                       Exit Do
                  End If
               Else
                    ' Errore
                    Exit Do
               End if
        Loop
        Close #[FileNum]
    End If
    InternetCloseHandle (hFile)
   ' Upload di un file
   ' Si richiede un handle per il file da inviare
    ' Non viene specificata una Callback --> modalitą blocking
    hFile = FtpOpenFile(hConnection, _
                [FileName], _
                GENERIC_WRITE, _
                FTP_TRANSFER_TYPE_BINARY _
                &H0)
    If hFile = 0 Then
        ' Errore
    Else
        ' Si procede con la scrittura del file.
        ' Occorre allocare un buffer per contenere i dati in arrivo
        [BufferLen] = 512
        [Buffer] = String([BufferLen], Chr$(0))
        ' Dato che il numero di byte che vengono effettivamente scritti
        ' puņ essere inferiore al numero di byte inviati, occorre tenere
        ' costantemente traccia della posizione del puntatore in lettura.
        [SrcFilePosition] = 1
        Open [FileName] For Binary Access Read As #[FileNum]
        ' Si calcola la dimensione del file in byte
        [FileLen] = LOF(#[FileNum])
        Do
             ' La variabile [BytesWritten] riporta il numero di byte che sono
             ' stati effettivamente scritti. Puņ essere differente da [BufferLen].
            Get #[FileNum], [SrcFilePosition], [Buffer]
             [Risposta] = InternetWriteFile(hFile, _
                                [Buffer], _
                                [BufferLen], _
                                [BytesWritten]
              If [Risposta] = True Then
                  ' La scrittura č andata a buon fine. Occorre verificare quanti
                  ' byte sono stati scritti e riprendere la lettura da quel punto
                  [SrcFilePosition] = [SrcFilePosition] + [BytesWritten]
                  If [SrcFilePosition] = [FileLen] Then
                      ' L'upload č completato
                      Exit Do
                  Else
                      ' Occorre verificare che non si stia leggendo oltre il file
                      If [SrcFilePosition] + [BufferLen] > [FileLen] Then
                          ' Nel caso, si riduce opportunamente la dimensione del buffer
                          [BufferLen] = [FileLen] - [SrcFilePosition]
                          [Buffer] = String([BufferLen], Chr$(0))
                      End If
                  End If
              Else
                  ' Errore
                  Exit Do
              End If
        Loop
        Close #[FileNum]
    End If
    InternetCloseHandle (hFile)
    InternetCloseHandle (hConnection)
    InternetCloseHandle (hInternet)
--------------------------------------------------------------
Per chi fosse interessato alla gestione degli eventi di connessione
tramite le Callback, suggerisco di dare un'occhiata alle funzioni
InternetSetStatusCallback(), che permette lo switching di una
Callback sugli handle di connessione corrente, e il prototipo
InternetStatusCallback() che permette di istanziare la Callback
di monitoraggio.
INTERNET_STATUS_CALLBACK InternetSetStatusCallback(
    IN HINTERNET hInternet,
    IN INTERNET_STATUS_CALLBACK lpfnInternetCallback
);
typedef VOID (CALLBACK * INTERNET_STATUS_CALLBACK)(
    IN HINTERNET hInternet,
    IN DWORD_PTR dwContext,
    IN DWORD dwInternetStatus,
    IN LPVOID lpvStatusInformation,
    IN DWORD dwStatusInformationLength
);