AsyncTasks: "FTP Download" / "FTP liste Daten in Verzeichnis" Probleme

lordzwieback

lordzwieback

Erfahrenes Mitglied
210
Guten Morgen,

ich habe bei zwei verschiedenen AsyncTasks einige Probleme und kann nicht herausfinden, woran es liegt. Wie der Überschrift zu entnehmen ist, handelt es sich einmal um eine Methode zum Download von Dateien von einem FTP Server und zum anderen um eine Methode, welche mir die Dateien aus meinem aktuellen Verzeichnis im Log aufzeichnet.

AsyncTask FTP Download:
Code:
// download file(s) from ftp server
    private class _async_ftp_downloadFile extends AsyncTask<String, Void, Boolean> {
        private String srcFilePath;
        private String dstFilePath;

        // constructor method
        public _async_ftp_downloadFile(String src, String dst) {
            this.srcFilePath = src;
            this.dstFilePath = dst;
        }

        @Override
        protected Boolean doInBackground(String... strings) {
            Boolean status = false;
            try {
                FileOutputStream dstFileStream = new FileOutputStream(dstFilePath);
                status = mFTPClient.retrieveFile(srcFilePath, dstFileStream);
                dstFileStream.close();
                Log.d(TAG, "Successfully downloaded file(s).");
                return status;
            } catch (Exception e) {
                Log.d(TAG, "Error: could not download file(s) from " + srcFilePath + " to " + dstFilePath);
            }
            return status;
        }

        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);

            if (result) {
                ergebnis.append("\n-- Daten wurden von " + srcFilePath + " nach " + dstFilePath + " transferiert.");
            } else {
                ergebnis.append("\n-- Daten konnten nicht von " + srcFilePath + " nach " + dstFilePath + " transferiert werden.");
            }
        }
    }
Hier springt er immer in die Exception -> Ausgabe "Error: could not download file from /home/XYOrdner to /storage/emulated/0/App_Ordner


AsyncTask "Liste Dateien aus Directory auf"
Code:
//list files from working directory into LOG
    private class _async_ftp_listFiles extends AsyncTask<String, Void, Void> {
        private String dir;

        // constructor
        public _async_ftp_listFiles(String directory) {
            this.dir = directory;
        }

        @Override
        protected Void doInBackground(String... strings) {
            try {
                FTPFile[] ftpFiles = mFTPClient.listFiles(dir);
                int length = ftpFiles.length;

                for (int i = 0; i<length; i++) {
                    String name = ftpFiles[i].getName();
                    boolean isFile = ftpFiles[i].isFile();

                    if (isFile) {
                        Log.i(TAG, "File: " + name);
                    } else {
                        Log.i(TAG, "Directory: " + name);
                    }
                }
            } catch (Exception e) {
                Log.e(TAG, "Error: Files and / or directory cant be listed.");
                e.printStackTrace();
            }
            return null;
        }
    }
Hier wird einfach nichts im Log angezeigt. Ja, es sind Dateien in den angegebenen Ordner drin (momentan eine Datei zum testen, Textdatei).

Falls die Frage aufkommt:
Meiner Manifest.xml habe ich folgende Zeilen schon eingebaut.
Code:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

EDIT:
Hier werden die AsyncTasks dann gestartet.
Code:
ftpSync.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ergebnis.setText("");
                new _async_ftp_conn(ftpHost.getText().toString(), ftpUsername.getText().toString(), ftpPassword.getText().toString()).execute();
                new _async_ftp_changeDir(ftpStandardDir.getText().toString()).execute();
                new _async_ftp_listFiles(ftpStandardDir.getText().toString()).execute();
                new _async_ftp_downloadFile(ftpStandardDir.getText().toString(), Environment.getExternalStorageDirectory() + "/Inspektionsmodul").execute();
                new _async_ftp_disc().execute();
            }
        });

Die Berechtigung habe ich dann in den Androideinstellungen unter App -> meine App -> Berechtigungen dann auch nochmal explizit eingeschaltet.

Für Tipps wäre ich sehr dankbar.

Grüße,
lordzwieback
 
Zuletzt bearbeitet:
Moin Moin ,

a) wie ermittelst du dir src & dst ? Gibt es die folder definitv ?? (Jedes Device hat unterschiedliche mounts)
b) was sagt das Ganze denn OHNE asyn

P.S mir ist aufgefallen , dass die die Async nacheinander mit execute ausführst - dir ist schon bewusst , dass
damit alle tasks auf das Beenden des vorherigen warten ?
 
@swa00

Wie ermittle ich den Quellpfad?
Ich habe ein TextEdit Element auf meiner "ftpActivity", welcher den Quellpfad enthält. Diesen hole ich beim Absenden des AsyncTasks mittels des Befehls
Code:
ftpStandardDir.getText().toString()

Wie ermittle ich den Zielpfad?
Den Zielpfad hole ich mir mittels dieses Befehls:
Code:
Environment.getExternalStorageDirectory() + "/Inspektionsmodul"

Der ganze Befehl:
Code:
new _async_ftp_downloadFile(ftpStandardDir.getText().toString(), Environment.getExternalStorageDirectory() + "/Inspektionsmodul").execute();

Wie meinst du das "ohne async"?

Zu deinem P.S:
Ja, das hattest du mir inklusive Syntax schon in einem früheren Post gesagt. Ich habe das wieder umgebaut, da beim Buttonklick folgendes passieren soll, wenn es fertig ist:
Verbindung herstellen, in Directory wechseln und je nach Einstellung bestimmte Daten runter- oder hochladen, danach Verbindung schließen. Deshalb war ich der Meinung, dass es nicht schlecht wäre, wenn die Verbindung zuerst hergestellt wird bevor versucht wird, die Daten herunterzuladen oder schon ins Verzeichnis zu wechseln. Alles schön nacheinander abarbeiten, dass keine Fehler entstehen war meine Idee.

EDIT: Ja, die Ordner existieren definitiv. Habe an anderer Stelle eine kleine Prüfung, ob der Ordner da ist und falls dieser nicht da ist, soll er erstellt werden. Über den Dateimanager meines Smartphones ist der Ordner auch einsehbar.
 
du machst ja im ersten Task

FileOutputStream dstFileStream = new FileOutputStream(dstFilePath);
dstFileStream.close();

Daher kommt auch meines Erachtens dein expetion ...
also LOKAL hast du den fehler -


Ich meinte damit : kannst du OHNE Task LOKAL schreiben und lesen. ?
Probier das mal aus .. lass mal das FTP Geraffel einfach weg - auch NICHT im ASYNC Testen
 
Werde das testen und mich danach nochmal melden. Danke für den Tipp.
[doublepost=1484561271,1484559581][/doublepost]@swa00 Ich hoffe mal ich habe das jetzt alles richtig verstanden und auch so getestet, wie du das gemeint hast.

Habe jetzt folgendes gemacht:

Normale Methode testDownload erstellt
Code:
public void testDownload() {
        String srcFilePath = "/home/Inspektionsmodul";
        String dstFilePath = Environment.getExternalStorageDirectory() + "/Inspektionsmodul";
        Boolean status;
        try {
            FileOutputStream dstFileStream = new FileOutputStream(dstFilePath);
            status = mFTPClient.retrieveFile(srcFilePath, dstFileStream);
            dstFileStream.close();
            Log.d(TAG, "Successfully downloaded file(s). status = " + status);
        } catch (Exception e) {
            Log.d(TAG, "Error: could not download file(s) from " + srcFilePath + " to " + dstFilePath);
        }
    }

Neuen Testbutton erstellt mit Aufruf der testDownload Methode
Code:
button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                testDownload();
            }
        });

Im "Sync" Button sind jetzt nur noch die AsyncTasks fürs Verbindung herstellen und Wechseln in gewünschtes Verzeichnis drin.

Testvorgang:
Ich klicke auf den "Sync" Button, Logs zeigen mir, dass ich mich mit meinem Server verbunden habe und dass ins angegebene Verzeichnis gewechselt wurde. Wenn ich dann auf meinen Testbutton klicke, erhalte ich die gleiche Meldung wie vorher: D/ftpActivity: Error: could not download file(s) from /home/Inspektionsmodul to /storage/emulated/0/Inspektionsmodul

EDIT: Vielleicht sollte ich mir angewöhnen, die eigentliche Exception Meldung mitauszugeben im Log.
java.io.FileNotFoundException: /storage/emulated/0/Inspektionsmodul: open failed: EISDIR (Is a directory)
 
@swa00 Ich habe nach einigem Googlen und zeilenweise Durchschauen meines Codes endlich den Fehler mehr oder weniger gefunden. Auf jeden Fall funktioniert es jetzt. Ich versuchs mal zu erklären:

Grundproblem war glaube ich, dass ich beim Wechseln meiner Directory folgenden Fehler gemacht habe.
Ursprüngliche Version:
Code:
try {
                mFTPClient.changeWorkingDirectory(this.dir);
                return bool;
            } catch (Exception e) {
                Log.d(TAG, "Error: could not change directory to " + dir);
            }
Hier habe ich einfach eine Boolsche Variable genommen und auf true gesetzt, wenn der changeWorkingDirectory Befehl keine Exception ausgelöst hat.

Jetzige Version:
Code:
try {
                dirExists = mFTPClient.changeWorkingDirectory(this.dir);
                return dirExists;
            } catch (Exception e) {
                Log.d(TAG, "Error: could not change directory to " + dir);
            }
Hier hole ich mir den return-Wert von changeWorkingDirectory direkt in die Variable dirExists (Bool).

Aufgrund dieses Fehlers und dem obligatorischen Fehler, der vor dem Bildschirm sitzt kam es zu diesem Folgefehler:
- Directory-Name auf FTP: TEST_Inspektionsmodul
- Directory-Name in meinem Kopf: Test_Inspektionsmodul

Dummerweise ist mir das nie vorher aufgefallen und da die boolsche Variable beim Ändern des Verzeichnis nie false angezeigt hat... tja, wohl dumm gelaufen.Deshalb liefen wohl die "Liste mir alle Files auf" und "Downloade mir Files aus WorkingDirectory" Async Tasks nie richtig, da das Verzeichnis nicht stimmte.

Außerdem habe ich noch meine Async Methode fürs Downloaden umgeschrieben. Die hätte, wenn ich das richtig verstanden habe immer nur eine Datei geladen. Hier also mein aktueller, funktionierender Download Async Code:
Code:
// download file(s) from ftp server
    private class _async_ftp_downloadFile extends AsyncTask<String, Void, Void> {
        private String srcFilePath;
        private String dstFilePath;

        // constructor method
        public _async_ftp_downloadFile(String src, String dst) {
            this.srcFilePath = src;
            this.dstFilePath = dst;
        }

        @Override
        protected Void doInBackground(String... strings) {
            try {
                FTPFile[] ftpFiles = mFTPClient.listFiles();
                // loop through files
                if (ftpFiles != null && ftpFiles.length > 0) {
                    for (FTPFile file : ftpFiles) {
                        Log.i(TAG, "File name: " + file.getName());
                        if (!file.isFile()) {
                            continue;
                        } else {
                            //Log.i(TAG, "file name = " + file.getName());
                            OutputStream dstFileStream = new FileOutputStream(dstFilePath + "/" + file.getName());
                            mFTPClient.retrieveFile(file.getName(), dstFileStream);
                            dstFileStream.close();
                        }
                    }
                    Log.d(TAG, "Successfully downloaded files from " + srcFilePath + " to " + dstFilePath);
                }
            } catch (Exception e) {
                Log.d(TAG, "Error: could not download file(s) from " + srcFilePath + " to " + dstFilePath);
                Log.d(TAG, "" + e);
            }
            return null;
        }

Vielleicht hilft es ja noch irgendwem irgendwann. :)

Falls jemand meinen Lösungsansatz korrigieren möchte, so möge er dies tun.
 
Hi lordzwieback,

wenn du ein Stream öffnest und es tritt ein Exception auf, muss du diesen in der Fehlerbehandlung wieder schließen. Du muss in der Catch Klausel überprüfen, ob der Stream geschlossen wurde, und das Objekt null ist.

zB. ... catch (Exception e) {
if (dstFileStream != null) {
dstFileStream.close();
}
}

Da neben ist der Konstruktor _async_ftp_downloadFile() überflüssig. Du brauchst keinen eigenen Konstruktor! (Außerdem werden Klassen in Java immer groß geschrieben, zB. AsyncFtpDownloadFile.)

Du rufst den Task mit new AsyncFtpDownloadFile().execute(src, dst); auf.

Auf die Variablen src und dst greift du in der Methode doInBackground() wie folgt zu:

String srcFilePath = strings[0];
String dstFilePath = strings[1];

Und noch eine Frage habe ich, kannst die App auf den externen Speicher überhaupt zugreifen. Bei neueren Android Versionen (ab 7.0) muss der User den Speicherzugriff explizit in den Einstellungen freigeben.
Using Scoped Directory Access | Android Developers
 

Ähnliche Themen

S
Antworten
7
Aufrufe
1.164
swa00
swa00
M
  • MikelKatzengreis
Antworten
5
Aufrufe
136
swa00
swa00
A
Antworten
10
Aufrufe
1.022
swa00
swa00
Zurück
Oben Unten