Http Upload zu CGI Script

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
milan1612
Beiträge: 810
Registriert: 15.04.2007 17:58

Http Upload zu CGI Script

Beitrag von milan1612 »

Ich hab hier ein CGI Script (Python) welches folgendes Formular ausgibt:

Code: Alles auswählen

<form action="\service.py" method="POST" enctype="multipart/form-data">
File name: <input name="file" type="file"><br>
<input name="submit" type="submit">
</form>
Per Browser funktioniert das Ganze prima - nur wie kann ich mit Purebasic damit Dateien hochladen?
Mir ist klar dass ich dafür nen Http Post machen muss, nur wie genau übergebe ich die Daten?

Danke schon mal :)

EDIT: Hab hier im Forum ein Beispiel gefunden, welches recht vielversprechend aussieht:

Code: Alles auswählen

InitNetwork()

ConnectionID = OpenNetworkConnection("www.domain.de", 80)

Content.s = "file: [dateiinhalt]"
buf$=buf$+"POST /service.py HTTP/1.0"+Chr(13)+Chr(10)
buf$=buf$+"Host: www.domain.de"+Chr(13)+Chr(10)
buf$=buf$+"Content-type: multipart/form-data"+Chr(13)+Chr(10)
buf$=buf$+"Content-length: "+Str(Len(Content))+Chr(13)+Chr(10)
buf$=buf$+Content+Chr(13)+Chr(10)
buf$=buf$+Chr(13)+Chr(10)

SendNetworkData(ConnectionID, buf$, Len(buf$))

CloseNetworkConnection(ConnectionID)
Bevor ich das hier gleich auf den Server loslasse, stimmt das soweit?
Bin nur noch sehr selten hier, bitte nur noch per PN kontaktieren
Benutzeravatar
milan1612
Beiträge: 810
Registriert: 15.04.2007 17:58

Re: Http Upload zu CGI Script

Beitrag von milan1612 »

Mal wieder son Post von mir in dem ich mein Problem selber löse... :lol:

Habs geschafft...
Bin nur noch sehr selten hier, bitte nur noch per PN kontaktieren
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Re: Http Upload zu CGI Script

Beitrag von AND51 »

Hi,

Ich hätte schon vor 5 ½ Stunden schreiben können, da hatte ich aber nur kurz Zeit, ins Forum zu schauen, ohne zu antworten.
Dafür kann ich ja noch jetzt schreiben.

Erstens ist dein Forumular nicht ganz korrrekt.
URL-Angaben enthalten keine Backslashes, sondern nur Slashes. Deshalb müsste es action="/service.py" heißen. Ich würde es so ändern, auch für den Fall, dass es trotzdem funktioniert. Nebenbei: Der Absendebutton braucht nicht zwingend einen Namen.

Deinen zusammengeklaubten HTTP-Request gehen wir allerdings jetzt nochmal durch.
Mich wundert es, dass es so klappt, der ist nämlich auch falsch.

Erstmal würde ich die aktuelle HTTP-Version 1.1 nehmen (1. Zeile), wobei das aber im Prinzip Wurscht ist. In der ersten Zeile hast du übrigens den richtigen Front- statt wie im Formular den Backslash gesetzt. In HTTP/1.1 ist die Angabe "Host" Pflicht, da sich heutzutage häufig mehrere Domains eine IP und damit einen Server teilen. Das heißt für dich: Ob nur zufällig oder absichtlich, die Host-Angabe sollte immer mit in den Header. Nur damit du das weißt. Dann kommt der Dateiinhaltstyp "Content-Type". Es gibt hier verschiedene, welchen Typ man exakterweise nehmen müsste, müsst ich selbst grad genauer recherchieren. Aber mit "multipart/form-data" liegst du schon richtig. Ist im Endeffekt aber egal, denn da du dein Phyton-Skript ja selbst kontrollierst, kannst du im Skript ja jeden Typ akzeptieren. Content-Length, das hast du richtig erkannt, ist ebenfalls eine Pflichtangabe (es sei denn, man nimmt einen anderen Requesttyp als POST) und bezeichnet die Dateigröße in Byte. Sobald der Request vollständig ist, wird er mit einer mit einer Leerzeile abgeschlossen! Und die fehlt bei dir bzw. steht an falscher Stelle (am Schluss). Merke: Nach den POST-Daten, also dem Dateiinhalt kommt rein gar nichts mehr, weder eine abschließende Leerzeile noch ein Zeilenumbruch.

Das war's.
Du könntest mit "Connection" noch wählen, ob die Verbindung offen bleiben ("keep-alive") oder geschlossen ("close") werden soll. Der Vorteil beim Offenlassen ist, dass die Verbindung nicht erneut aufgebaut werden muss, solltest du eh mehrere Dateien übertragen wollen.
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
milan1612
Beiträge: 810
Registriert: 15.04.2007 17:58

Re: Http Upload zu CGI Script

Beitrag von milan1612 »

Hi AND,

danke für deine Antwort. Deine Kritikpunkte sind alle richtig (soweit ich das überblicke),
doch der obige Code ist nicht das, was ich am Ende benutzt habe.

Ich hab mit einem HTTP Analyzer den Request von Firefox mitgeschnitten und dann
angeändert für mein Programm verwendet. Hier mal ein Ausschnitt des Codes:

Code: Alles auswählen

Procedure Upload(*mem, fname.s)
  Protected ConnectionID = OpenNetworkConnection(GetHost(), 80)
  
  Protected request.s = "POST /service.py HTTP/1.1" + #CRLF$
  request + "Host: " + Chr(34) + GetHost() + Chr(34) + #CRLF$
  request + "Content-type: multipart/form-data; boundary=---------------------------41184676334" + #CRLF$
  request + "Content-length: "
  
  Protected content_begin.s = "-----------------------------41184676334" + #CRLF$
  content_begin + "Content-Disposition: form-data; name=" + Chr(34) + "file" + Chr(34) + "; filename=" + Chr(34) + fname + Chr(34) + #CRLF$
  content_begin + "Content-Type: application/octet-stream" + #CRLF$ + #CRLF$
  
  Protected content_end.s = #CRLF$ + "-----------------------------41184676334" + #CRLF$
  content_end + "Content-Disposition: form-data; name=" + Chr(34) + "submit" + Chr(34) + #CRLF$ + #CRLF$
  content_end + "Submit Query" + #CRLF$
  content_end + "-----------------------------41184676334--" + #CRLF$
  
  Protected ContentLength = Len(content_begin) + MemorySize(*mem) + Len(content_end)
  request + Str(ContentLength) + #CRLF$ + #CRLF$
  
  Protected *buff = AllocateMemory(Len(request) + Len(content_begin) + MemorySize(*mem) + Len(content_end))
  If *buff
    CopyMemory(@request, *buff, Len(request))
    CopyMemory(@content_begin, *buff + Len(request), Len(content_begin))
    CopyMemory(*mem, *buff + Len(request) + Len(content_begin), MemorySize(*mem))
    CopyMemory(@content_end, *buff + Len(request) + Len(content_begin) + MemorySize(*mem), Len(content_end))
    
    SendNetworkData(ConnectionID, *buff, MemorySize(*buff))
    
    FreeMemory(*buff)
  EndIf

  CloseNetworkConnection(ConnectionID)
EndProcedure
Hab ein bisschen Verwaltungscode herausgenommen und das Ganze anonymisiert, aber
so in etwa schaut es jetzt aus (und funktioniert :D ).
Bin nur noch sehr selten hier, bitte nur noch per PN kontaktieren
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Re: Http Upload zu CGI Script

Beitrag von AND51 »

Stimmt, "application/octect-stream" ("Binärer Datenstrom") ist das, wonach ich neben "multipart/form-data" ("mehrteilige Formulardaten") gesucht hatte!
Jetzt, wo ich es sehe, fällt es mir wieder ein.

Nun, das mit dem "boundary" kenne ich nicht, ehrlich gesagt. Ich hatte mal eine Facharbeit über das HTTP-Protokoll geschrieben und kenne mich daher mit der Materie aus, aber da ist mir ein "boundary" nie begegnet. Mag sein, dass es funktioniert, aber ich würde den darin sehen, dass man Daten gezielt getrennt übertragen kann, je nach Dateityp und -menge. Ich will es dir nicht schlechtreden, aber ich würde es weglassen und alle Daten als einen Datenstrom senden. Irgendwie finde ich das besser und sicherer. Bin nicht sicher, ob das "boundary" da reingehören darf oder ob das ne FireFox-spezifische Sache ist.
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
milan1612
Beiträge: 810
Registriert: 15.04.2007 17:58

Re: Http Upload zu CGI Script

Beitrag von milan1612 »

"boundary" ist eine absolut offizielle Methode um "Multipart" Requests durchzuführen.
Siehe hier: http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
Bin nur noch sehr selten hier, bitte nur noch per PN kontaktieren
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Re: Http Upload zu CGI Script

Beitrag von AND51 »

OK, ich seh schon ich muss mich nochmal genauer reinarbeiten^^
Danke für die Info, milan!

Hey, es reichten 12 Seiten (abzüglich Quellenangabe-Seite, Inhaltsverzeichnis, Titelblatt, große Schrift (12pt), etc.). Wir hatten 6 Wochen Zeit und ich habe mich die Nacht vor dem Abgabetag hingesetzt und alles aus dem Kopf geschrieben, lediglich mit Code-Beispielen aber ohne Bilder! Hab trotzdem meine 1- kassiert, wegen ein paar Typos hier und da!
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
dige
Beiträge: 1183
Registriert: 08.09.2004 08:53

Re: Http Upload zu CGI Script

Beitrag von dige »

Hallo milan1612,

vielen Dank für dein Beispiel. Bin gerade auf der Suche nach einem Upload Beispiel,
da ich aus einem PB Programm eine Datei an einen Webserver senden möchte.

Ich bekomme leider vom Server immer ein Bad Request

Code: Alles auswählen

InitNetwork()

Procedure Upload(*mem, fname.s)
  Protected ConnectionID = OpenNetworkConnection(fname, 80)
  
  Debug ConnectionID
  
  Protected request.s = "POST /cgi-bin/cgi.exe?0=CGI_File_Upload HTTP/1.1" + #CRLF$
  request + "Host: " + Chr(34) + fname + Chr(34) + #CRLF$
  request + "Content-type: multipart/form-data; boundary=---------------------------41184676334" + #CRLF$
  request + "Content-length: "
 
  Protected content_begin.s = "-----------------------------41184676334" + #CRLF$
  content_begin + "Content-Disposition: form-data; name=" + Chr(34) + "file" + Chr(34) + "; filename=" + Chr(34) + fname + Chr(34) + #CRLF$
  content_begin + "Content-Type: application/octet-stream" + #CRLF$ + #CRLF$
 
  Protected content_end.s = #CRLF$ + "-----------------------------41184676334" + #CRLF$
  content_end + "Content-Disposition: form-data; name=" + Chr(34) + "submit" + Chr(34) + #CRLF$ + #CRLF$
  content_end + "Submit Query" + #CRLF$
  content_end + "-----------------------------41184676334--" + #CRLF$
 
  Protected ContentLength = Len(content_begin) + MemorySize(*mem) + Len(content_end)
  request + Str(ContentLength) + #CRLF$ + #CRLF$
 
  Protected *buff = AllocateMemory(Len(request) + Len(content_begin) + MemorySize(*mem) + Len(content_end))
  If *buff
    CopyMemory(@request, *buff, Len(request))
    CopyMemory(@content_begin, *buff + Len(request), Len(content_begin))
    CopyMemory(*mem, *buff + Len(request) + Len(content_begin), MemorySize(*mem))
    CopyMemory(@content_end, *buff + Len(request) + Len(content_begin) + MemorySize(*mem), Len(content_end))
    
    Debug "Sending.." + StrU(MemorySize(*buff)/1024/1024) + "MB"
    Debug SendNetworkData(ConnectionID, *buff, MemorySize(*buff))
    
    FreeMemory(*buff)
  EndIf
  
  *get = AllocateMemory(64000)
  
  Debug "Receive.."
  
  Debug ReceiveNetworkData(ConnectionID, *get, 64000)
  
  Debug PeekS(*get, -1, #PB_UTF8 )
  
  CloseNetworkConnection(ConnectionID)
EndProcedure

file.s = OpenFileRequester("Choose file upload", "", "Alle Dateien (*.*)|*.*", 0)
If ReadFile(0, file)
  *mem = AllocateMemory(Lof(0))
  ReadData(0, *mem, Lof(0))
  CloseFile(0)
EndIf

Upload(*mem, "myHost") ; myHost entsprechend ändern

Code: Alles auswählen

HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Tue, 24 Sep 2019 16:45:03 GMT
Connection: close
Content-Length: 326

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Bad Request - Invalid Verb</h2>
<hr><p>HTTP Error 400. The request verb is invalid.</p>
</BODY></HTML>
Funktioniert das eigentlich auch mit https oder muss man das lieber mit Curl machen?
"Papa, mein Wecker funktioniert nicht! Der weckert immer zu früh."
NeoChris
Beiträge: 205
Registriert: 21.11.2013 21:17
Wohnort: Schweiz
Kontaktdaten:

Re: Http Upload zu CGI Script

Beitrag von NeoChris »

Schade dass beide Nutzer milan1612 und AND51 seit Jahren nicht mehr online waren.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Http Upload zu CGI Script

Beitrag von NicTheQuick »

@dige:
Das Problem wird sein, dass Purebasic-Strings Unicode sind und wenn du sie mit SendNetworkData binär verschickst, dann ist jedes zweite Byte 0. Somit ist dein Request-Header schon ungültig. Versuche den Request vorher mit Ascii() oder UTF8() umzuwandeln.
Bild
Antworten