Arduino Mega - Download Dateien von SD-Card via Browser

Hallo zusammen,

ich habe einen Arduino Mega mit Ethernet-Shield. In dem Ethernet-Shield ist eine SD-Karte gesteckt.
Ich möchte erreichen dass ich die Dateien auf der SD-Card via Browser herunterladen kann.

Die Dateien auf der SD werden mir im Browser schon angezeigt, aber ein einfaches
client.println "<a href=/IMAGE01.jpg>IMAGE01.jpg</a>"; reicht irgendwie nicht. Die Datei wird zwar als Link angezeigt, aber das
einzige was heruntergeladen wird ist eine Datei "IMAGE01.jpg" welche den HTML-Quellcode der Seite enthält, die mein Arduino mir anzeigt....

Hat jemand einen Lösungsvorschlag?

Postest Du den ganzen Sketch? Ohne das Programm wissen wir ja nicht, was Du auslieferst.

Wenn ich mal etwas pingelig sein darf, dann sollte es so aussehen:

client.println "<a href=\"/IMAGE01.jpg\">IMAGE01.jpg</a>";

Wenn Du das Bild einbetten willst, sollte es so aussehen:

client.println "<img src=\"/IMAGE01.jpg\" />";

Die Datei wird zwar als Link angezeigt, aber das
einzige was heruntergeladen wird ist eine Datei "IMAGE01.jpg" welche den HTML-Quellcode der Seite enthält, die mein Arduino mir anzeigt....

Dann dürftest Du den Versuch mit einem IE gemacht haben, richtige Browser halten sich an den Mime-Type, der vom Server geliefert wird und somit hoffentlich (habe Deinen Code ja noch nicht gesehen) "text/html" heisst.

Hier mein HTML Code:

EthernetClient client = server.available();
  if (client) {
    Serial.println("Neuer HTTP Client");
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
         if (readString.length() < 100) {
                   readString = readString + c; 
         }
       if (c == '\n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connnection: close");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html><head>");
          client.print("<title>File Download</title>");
          client.println("</head>");
          client.println("<body>");

client.println("<p>");
          client.println("<p>");
          /// Bilder anzeigen 
           root = SD.open("/");
              while(true) {
                   File entry =  root.openNextFile();
                   if (! entry) {
                   // no more files
                   //Serial.println("**nomorefiles**");
                   break;
                   }
                   Serial.println(entry.name());
                   client.println();
                   client.print("<a href=");
                   client.print(entry.name());
                   client.print(">");
                   client.print(entry.name());
                   client.print("</a>");
                   client.println("
");
             }
           client.print("
</font>");   
          client.print("</body>");
          client.print("</html>");

DAuf der SD-Card befinden nur Dateien "IMAGE00.jpg", "IMAGE01.jpg", "IMAGE02.jpg"

Hallo Thombo84,

du musst eine Unterscheidung machen was für eine Anfrage an den Webserver gestellt wird und dann die Datei anstelle der Webseite zurückliefern.

Wenn ein GET ankommt ohne dass ein weiterer Befehl angehängt ist, so gebe ich die Dateiliste aus.
Klicke ich eine Datei an, so bekomme ich eine neuen Anfrage mit dem Dateinamen. Diesen kann ich abprüfen und sofern diese Datei existiert zurückgeben.
Dann habe ich noch einen Fall mehr eingebaut, der ist natürlich nicht unbedingt nötig. Und zwar kann ich auch eine Steuerung vornehmen in dem ich Befehle mitsende. Die werden mit einem GET? angekündigt.

 // Look for substring such as a request to get the root file
        if (strstr(clientline, "GET / ") != 0)
        {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("<html><body bgcolor='#444444' text='#c0c0c0'>");
          client.println();
          
          // print all the files, use a helper to keep it clean
          client.println("<h2>Files:</h2>");
          ListFiles(client, LS_SIZE);
          client.println("<p><a href='INDEX.HTM'>zur&uuml;ck</a></p></body></html>");
        } 
        else if (strstr(clientline, "GET /") != 0) 
        {
          // this time no space after the /, so a sub-file!
          char *filename;
          
          filename = clientline + 5; // look after the "GET /" (5 chars)
          // a little trick, look for the " HTTP/1.1" string and 
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
          
          // print the file we want
//          Serial.println(filename);
          // File couldn´t be opened
          if (! file.open(&root, filename, O_READ)) 
          {            
            //If it is an command, work with it
            if(filename[0] == '?')
            {
              //Function to control Pins, ect.
              ProcessInput(client, filename);
              break;
            }              
            else
            {
            // Print 404
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>File Not Found!</h2>");
            }
            break;            
          }
          
          //It really is a File
//          PgmPrint("Opened!");                    
          client.println("HTTP/1.1 200 OK");
          
          if(strstr(clientline, "HTM") != 0 || strstr(clientline, "TML") != 0)
            client.println("Content-Type: text/html");          
          else
            client.println("Content-Type: text/plain");
          client.println();
          
          int16_t c;
          while ((c = file.read()) > 0)
          {
              // uncomment the serial to debug (slow!)
              //Serial.print((char)c);
              client.print((char)c);
          }
          file.close();
        } 
        else 
        {
          // everything else is a 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>File Not Found!</h2>");
        }

Was ich darüber hinaus noch gemacht habe ist, dass ich unterscheide ob die Datei eine richtige Datei ist oder eine HTML-Seite. Dann kann man im Header den Content-Type setzen und die Seite wird direkt ausgegeben. Ansonsten sieht man nur den Quelltext. Funktioniert mit Bildern hervorragend!

Gruß,
trib

Und wo ist der Code der ein Bild an den Client (Browser) liefert? Wenn Du einen Link auf der von Arduino gelieferten HTML-Seite anklickst, dann schickt der Browser wieder einen HTTP-Request an den Arduino mit der URI (Pfad aus dem Link) der verlinkten Datei. Dein Programm muss also auf ein "GET /IMAGE00.jpg" reagieren, die Datei öffnen und die entsprechenden Daten an den Browser schicken.
Zur Zeit reagiert Dein Programm vermutlich nur mit der einen Programmierten HTML-Seite auf alle ankommenden Requests.

Trib:
Hallo Thombo84,

du musst eine Unterscheidung machen was für eine Anfrage an den Webserver gestellt wird und dann die Datei anstelle der Webseite zurückliefern.

Wenn ein GET ankommt ohne dass ein weiterer Befehl angehängt ist, so gebe ich die Dateiliste aus.
Klicke ich eine Datei an, so bekomme ich eine neuen Anfrage mit dem Dateinamen. Diesen kann ich abprüfen und sofern diese Datei existiert zurückgeben.
Dann habe ich noch einen Fall mehr eingebaut, der ist natürlich nicht unbedingt nötig. Und zwar kann ich auch eine Steuerung vornehmen in dem ich Befehle mitsende. Die werden mit einem GET? angekündigt.

 // Look for substring such as a request to get the root file

if (strstr(clientline, "GET / ") != 0)
        {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("");
          client.println();
         
          // print all the files, use a helper to keep it clean
          client.println("

Files:

");
          ListFiles(client, LS_SIZE);
          client.println("

zurück

");
        }
        else if (strstr(clientline, "GET /") != 0)
        {
          // this time no space after the /, so a sub-file!
          char *filename;
         
          filename = clientline + 5; // look after the "GET /" (5 chars)
          // a little trick, look for the " HTTP/1.1" string and
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
         
          // print the file we want
//          Serial.println(filename);
          // File couldn´t be opened
          if (! file.open(&root, filename, O_READ))
          {           
            //If it is an command, work with it
            if(filename[0] == '?')
            {
              //Function to control Pins, ect.
              ProcessInput(client, filename);
              break;
            }             
            else
            {
            // Print 404
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("

File Not Found!

");
            }
            break;           
          }
         
          //It really is a File
//          PgmPrint("Opened!");                   
          client.println("HTTP/1.1 200 OK");
         
          if(strstr(clientline, "HTM") != 0 || strstr(clientline, "TML") != 0)
            client.println("Content-Type: text/html");         
          else
            client.println("Content-Type: text/plain");
          client.println();
         
          int16_t c;
          while ((c = file.read()) > 0)
          {
              // uncomment the serial to debug (slow!)
              //Serial.print((char)c);
              client.print((char)c);
          }
          file.close();
        }
        else
        {
          // everything else is a 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("

File Not Found!

");
        }




Was ich darüber hinaus noch gemacht habe ist, dass ich unterscheide ob die Datei eine richtige Datei ist oder eine HTML-Seite. Dann kann man im Header den Content-Type setzen und die Seite wird direkt ausgegeben. Ansonsten sieht man nur den Quelltext. Funktioniert mit Bildern hervorragend!

Gruß,
trib

Danke für den Hinweis :slight_smile:
Kleine Frage am Rande: Als was hast du "file" deklariert?
Ich versuche das gerade an meinen Sketch anzubinden, bekomme bei file.open(&root, entry.name(), O_READ))
aber "file was not declared in this scope"

mkl0815:
Und wo ist der Code der ein Bild an den Client (Browser) liefert? Wenn Du einen Link auf der von Arduino gelieferten HTML-Seite anklickst, dann schickt der Browser wieder einen HTTP-Request an den Arduino mit der URI (Pfad aus dem Link) der verlinkten Datei. Dein Programm muss also auf ein "GET /IMAGE00.jpg" reagieren, die Datei öffnen und die entsprechenden Daten an den Browser schicken.
Zur Zeit reagiert Dein Programm vermutlich nur mit der einen Programmierten HTML-Seite auf alle ankommenden Requests.

Der Code liefert Dir jede Art von Datei zurück. Ob Bild, Text oder meinetwegen auch einer *.exe-Anwendung.
An folgender Stelle wird unterschieden ob es sich um eine HTML-Seite handelt und wenn nicht, wir die Datei direkt an den Browser zurück übermittelt. Genau wie Du ansprichst, wird geprüft ob noch was hinter dem GET kommt und entsprechend ausgelesen. Funktioniert bei mir problemlos!

if(strstr(clientline, "HTM") != 0 || strstr(clientline, "TML") != 0)
            client.println("Content-Type: text/html");          
          else
            client.println("Content-Type: text/plain");
          client.println();
          
          int16_t c;
          while ((c = file.read()) > 0)
          {
              client.print((char)c);
          }
          file.close();

Das setzen des Content-Type auf "text/plain" ist der Trick.

@Thombo84: Das habe ich aus diesem Tutorial. Damit ist es Problemlos möglich die SD-Karte und das Ethernet Modul anzusprechen.
Ansonsten müsstest Du mal schauen was die SD-Library noch so bietet, die Du eingebunden hast.

root = SD.open("/");

Hier initialisierst du ja die Karte und ziehst Dir die Dateiliste. Sicherlich gibt es auch eine ähnliche Funktion, wie das file.read was ich aus dem Tutorial nutze.

@Trib: Meine Frage bezog sich auf das originale Posting, nicht auf Deinen Code. Unsere Posts haben sich nur überschnitten.

mkl0815:
@Trib: Meine Frage bezog sich auf das originale Posting, nicht auf Deinen Code. Unsere Posts haben sich nur überschnitten.

Dann nehme ich alles zurück und behaupte das Gegenteil :smiley:

Thombo84:
Hat jemand einen Lösungsvorschlag?

Eigne Dir ein paar Grundkenntnisse über das HTTP-Protokoll an!

Das kann bei solchen Sachen wie Du sie vorhast, nämlich kein bisschen schaden.

Du müßtest nämlich irgendwie die Anfrage des Browsers auswerten, WAS der Browser eigentlich genau vom Webserver anfordert. Siehe auch:
http://playground.arduino.cc/Code/WebServer

Wenn es Deinem Sketch völlig egal ist, was der Browser anfordert, weil Du das gar nicht auswertest, und Du einfach immer dasselbe an den Client zurücksendest, dann bekommt der Client auch immer dasselbe vorgesetzt.

Du machst im Prinzip so was ähnliches wie der Ober im Monty Python "Spam"-Sketch: Egal was der Gast als Gericht bestellen möchte - er bekommt auf jeden Fall Spam! Und egal was der Browser von Deinem Arduino anfordert, er bekommt auf jeden Fall auch immer dasselbe.