Entwickler-Ecke
Internet / Netzwerk - FtpPutFile und Stream?
LittleBen - So 26.08.12 15:44
Titel: FtpPutFile und Stream?
Guten Mittag,
ich arbeite zurzeit mit den Funktionen von WinInet und zwar für FTP-Uplaods (bewusst ohne Indy). Jetzt möchte ich gerne den Inhalt eines Memos hochladen und das bestmöglich ohne eine temporäre Datei. Das geht natürlich nur mit einem Stream...Gibt es irgendeinen Weg, einen Stream mit FtpPutFile hochzuladen?
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| function FtpPutFile( hConnect: HINTERNET; lpszLocalFile: PChar; lpszNewRemoteFile: PChar; dwFlags: DWORD; dwContext: DWORD): BOOL; |
Noch einen schönen Sonntag und Grüße,
Littleben
jaenicke - So 26.08.12 16:14
Siehe Dokumentation:
Einfacher und vor allem plattformunabhängig wäre es natürlich mit z.B. Indy, aber da du das nicht nutzen möchtest...
Indy setzt übrigens nicht auf die API auf sondern kommuniziert direkt mit dem FTP-Server.
LittleBen - So 26.08.12 21:13
ahhh ^^ Ok, vielen Dank!
Könntest du mir noch "kurz" sagen, was ich beim Lesevorgang falsch mache?
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| procedure UploadData(RemoteFile: string; Data: TMemoryStream); var hOpen, hUrl: HINTERNET; Buffer: array[0..1024] of Char; BytesRead: dWord; Data_written: Cardinal; begin hOpen:= InternetOpen(PChar(FDesc), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); hUrl:= InternetOpenUrl(hOpen, PChar(FHost), nil, 0, INTERNET_FLAG_RAW_DATA, 0);
FillChar(Buffer, SizeOf(Buffer), 0); repeat FillChar(Buffer, SizeOf(Buffer), 0); Data.ReadBuffer(Buffer,sizeOf(Buffer)); InternetWriteFile(hUrl,addr(Buffer),sizeof(Buffer),Data_Written); until (Data_Written<=0) or (data.Position>=data.Size);
InternetCloseHandle(hurl); InternetCloseHandle(hOpen); end; |
Bekomme dabei den Fehler "Stream-Lesefehler"
Gerd Kayser - Mo 27.08.12 00:03
LittleBen hat folgendes geschrieben : |
Bekomme dabei den Fehler "Stream-Lesefehler" |
Hast Du vielleicht vergessen, vor dem Lesen aus dem Stream die Position zu setzen?
jaenicke - Mo 27.08.12 07:49
Das wird es wohl sein, ja. Ich würde die verbleibende Datengröße im Stream aber auch beim Lesen berücksichtigen statt einfach zu versuchen so viele Bytes auszulesen wie in den Puffer passen. ;-)
SvenAbeln - Mo 27.08.12 09:01
Auch beim Schreiben mit InternetWriteFile sollte die wirkliche Anzahl an Bytes berücksichtigt werden. Im Moment schreibst du immer den kompletten Buffer, auch wenn du z.B. nur ein Byte aus dem Stream gelesen hast.
LittleBen - Mo 27.08.12 09:58
Mhm, die Position vor der Schleife zu setzten hat auch nichts gebracht...ein Seek-Befehl genau so nicht.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| procedure UploadData(RemoteFile: string; Data: TMemoryStream); var hOpen, hUrl: HINTERNET; Buffer: array[0..1024] of Char; BytesRead: dWord; Data_written: Cardinal; begin hOpen:= InternetOpen(PChar(FDesc), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); hUrl:= InternetOpenUrl(hOpen, PChar(FHost), nil, 0, INTERNET_FLAG_RAW_DATA, 0);
Data.Position:= 0; FillChar(Buffer, SizeOf(Buffer), 0); repeat FillChar(Buffer, SizeOf(Buffer), 0); Data.ReadBuffer(Buffer,sizeOf(Buffer)); InternetWriteFile(hUrl,addr(Buffer),sizeof(Buffer),Data_Written); until (Data_Written<=0) or (data.Position>=data.Size);
InternetCloseHandle(hurl); InternetCloseHandle(hOpen); |
SvenAbeln - Mo 27.08.12 13:11
ReadBuffer prüft ob genau die angegebene Anzahl Bytes gelesen wurde und gibt sonst einen "Stream-Lesefehler". Falls du also nicht genau 1025 Char in deinem Stream hast, wirst du so immer diesen Fehler bekommen.
Read liest einfach die Bytes und gibt dabei keinen Fehler aus.
LittleBen - Mo 27.08.12 14:17
Ok, ein Fehler bekomm ich nun nicht mehr, aber es wird komischerweise nichts ausgelesen?
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:
| var hOpen, hUrl: HINTERNET; BufferIn : INTERNET_BUFFERS; Buffer: array[0..1024] of Byte; TmpStream: TMemoryStream; cURL: string; Size: integer; BytesWritten: cardinal; begin TmpStream:= TMemoryStream.Create; Form1.Memo1.lines.SaveToStream(TmpStream); cURL:= 'http://test.de/test.dat';
hOpen := InternetOpen('Test', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); hUrl := InternetOpenUrl(hOpen, PChar(cURL), nil, 0, INTERNET_FLAG_RAW_DATA, 0);
try BufferIn.dwBufferTotal:= TmpStream.Size; Size:= TmpStream.Read(Buffer, 1024); InternetWriteFile(hUrl, @Buffer, size, BytesWritten);
while (BytesWritten = Size) and (BytesWritten > 0) do begin size:= TmpStream.Read(Buffer, 1024); InternetWriteFile(hUrl, @Buffer, Size, BytesWritten); end; finally TmpStream.Free;
InternetCloseHandle(hUrl); InternetCloseHandle(hOpen); end; |
LittleBen - Mi 29.08.12 13:55
Hier nun die funktionierende Funktion:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:
| function TUploader.UploadData(Source: string; RemoteFile: string): boolean; var hOpen, hConnect, hFile: HINTERNET; Size: integer; Index: integer; toWrite: cardinal; BytesWritten: cardinal; const Blocksize = 1024; begin hOpen:= InternetOpen(PChar(FDesc), INTERNET_OPEN_TYPE_DIRECT, nil, nil, 0); hConnect:= InternetConnect(hOpen, PChar(FHost), FPort, PChar(FUsername), PChar(FPassword), INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); hFile:= FtpOpenFile(hConnect, PChar(RemoteFile), GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY, 0); try if Assigned(hFile) then begin Size:= Length(Source); Index:= 1; repeat toWrite:= Size - Index + 1; if toWrite>Blocksize then toWrite:= Blocksize; BytesWritten:= 0; if InternetWriteFile(hFile,@source[Index],toWrite,BytesWritten) then inc(Index,BytesWritten); until (BytesWritten < Blocksize) or (Index>Size); result:= true; end; finally InternetCloseHandle(hFile); InternetCloseHandle(hConnect); InternetCloseHandle(hOpen); end; end; |
Hat jemand eine Idee, wie ich den String nur hinzufügen kann ohne die Datei komplett zu überschreiben (also Append)? Erst auslesen und dann wieder komplett schreiben ist nicht ideal, da der Upload nicht gerade der schnellste ist...
SOLVED:
http://support.microsoft.com/kb/182316/de
jaenicke - Mi 29.08.12 19:39
Das ganze liest sich so als ob du gar kein FTP brauchst, sondern eher ein Skript auf dem Server, das die Datei schreibt...
Dann hast du auch nicht das Problem, dass du die Logindaten mit dem Programm mitliefern musst, was bei FTP ja unumgänglich ist. (Die lassen sich da ja blitzschnell auslesen, egal was du machst.)
LittleBen - Mi 29.08.12 19:43
Mhm, stimmt. Also wäre es eine Alternative einfach via Post die Daten an eine PHP-Datei zu schicken?
jaenicke - Mi 29.08.12 20:12
Richtig. Da kann dann zumindest nicht viel passieren, wenn jemand die Datei herausfindet, er kann höchstens die Datei vollmüllen.
Zudem ist es sehr viel einfacher.
LittleBen - Mi 29.08.12 20:27
Okay, das werde ich nacher gleich mal probieren. Davor würde ich trotzdem noch gerne die Geschichte mit dem FTP zuende bringen, es ist ja eigentlich nicht mehr weit vom Ziel entfernt ^^ Der folgende Code gibt ein Error beim
FtpCommand aus. Um genauer zu sein den Errorcode 12003, was folgendes bedeutet:
Zitat: |
ERROR_INTERNET_EXTENDED_ERROR
Vom Server wurde ein erweiterter Fehler zurückgegeben. Hier handelt es sich in der Regel um eine Zeichenfolge oder einen Puffer, der eine ausführliche Fehlermeldung enthält.
Rufen Sie "Call InternetGetLastResponseInfo" auf, um den
Fehlertext zu erhalten. |
Als
InternetGetLastResponseInfo bekomme ich zurück:
Zitat: |
200 Type set to A
227 Entering Passive Mode (210,202,225,203,197,39)
451 Test.dat: Append/Restart not permitted, try again |
Weiß jemand, an was das liegt? Muss ich mich anderst anmelden?
CODE:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49:
| var hOpen, hConnect, hFile: HINTERNET; Size: integer; Index: integer; toWrite: cardinal; BytesWritten: cardinal; dwError : DWord; szBuffer : PChar; dwSize : DWord; const Blocksize = 1024; begin hOpen:= InternetOpen(PChar(FDesc), INTERNET_OPEN_TYPE_DIRECT, nil, nil, 0); hConnect:= InternetConnect(hOpen, PChar(FHost), FPort, PChar(FUsername), PChar(FPassword), INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
if Append then if Assigned(hConnect) then if not FtpCommand(hConnect, true, FTP_TRANSFER_TYPE_ASCII, PChar('APPE '+RemoteFile), 0) then begin showmessage(inttostr(GetLastError)); dwSize:= 255; szBuffer:= StrAlloc(dwSize); try InternetGetLastResponseInfo(dwError, szBuffer, dwSize); showmessage(szBuffer); finally StrDispose(szBuffer); end; end;
hFile:= FtpOpenFile(hConnect, PChar(RemoteFile), GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY, 0); try if Assigned(hFile) then begin Size:= Length(Source); Index:= 1; repeat toWrite:= Size - Index + 1; if toWrite>Blocksize then toWrite:= Blocksize; BytesWritten:= 0; if InternetWriteFile(hFile,@source[Index],toWrite,BytesWritten) then inc(Index,BytesWritten); until (BytesWritten < Blocksize) or (Index>Size); result:= true; end; finally InternetCloseHandle(hFile); InternetCloseHandle(hConnect); InternetCloseHandle(hOpen); end; |
jaenicke - Mi 29.08.12 22:31
Du musst dies am FTP-Server konfigurieren. Standardmäßig ist das oft deaktiviert.
LittleBen - Mi 29.08.12 23:08
Mhm, ich habe als Testserver einen FTP-Server bei Ohost. Der Support behauptet es liege am Client. Man kann sonst auch nichts am Server einstellen... :(
EDIT: Hab hier noch etwas interessantes gefunden, kann es aber nicht richtig deuten ^^
Zitat: |
Syntax: APPE remote-filename
Append data to the end of a file on the remote host. If the file does not already exist, it is created. This command must be preceded by a PORT or PASV command so that the server knows where to receive data from. |
http://www.nsftools.com/tips/RawFTP.htm#APPE
Zitat: |
Syntax: PORT a1,a2,a3,a4,p1,p2
Specifies the host and port to which the server should connect for the next file transfer. This is interpreted as IP address a1.a2.a3.a4, port p1*256+p2. |
http://www.nsftools.com/tips/RawFTP.htm#PORT
Zitat: |
Syntax: PASV
Tells the server to enter "passive mode". In passive mode, the server will wait for the client to establish a connection with it rather than attempting to connect to a client-specified port. The server will respond with the address of the port it is listening on, with a message like:
227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
where a1.a2.a3.a4 is the IP address and p1*256+p2 is the port number. |
http://www.nsftools.com/tips/RawFTP.htm#PASV
jaenicke - Do 30.08.12 09:34
Also ich kann es auf meinem dedicated Server einstellen und auf meinem Webspace wird das unterstützt, habe es bei 3 Servern ausprobiert (nicht mit dem Quelltext, auf der Konsole). ;-)
Bei einem Freehoster brauchst du dich nicht wundern... die paar Euro würde ich schon investieren.
Aber davon abgesehen ist die andere Lösung doch in 20 Minuten fertig und sowieso sinnvoller. ;-)
jaenicke - Do 30.08.12 09:36
// EDIT: Doppelpost...
LittleBen - Do 30.08.12 11:49
Nagut, dann lass ich das mit dem FTP sein ^^ Zudem ist die Parameterliste in WinInet auch noch falsch...der letzte Parameter fehlt.
Gibt es eine begrenzte Datenmenge die man mit Post übermitteln kann? Und fallen am Server nicht noch größere Lasten an, wenn alles noch über eine PHP-Datei geht?
jaenicke - Do 30.08.12 12:26
LittleBen hat folgendes geschrieben : |
Zudem ist die Parameterliste in WinInet auch noch falsch...der letzte Parameter fehlt. |
Bei Delphi 7, ja. In aktuellen nicht soweit ich das sehe. ;-)
LittleBen hat folgendes geschrieben : |
Gibt es eine begrenzte Datenmenge die man mit Post übermitteln kann? |
Nicht direkt. Ein Timeout wäre möglich, wenn es zu lange dauert, je nach Servereinstellung. Größere Datenmengen würde ich aber als Dateiupload hochladen, da gehen je nach Server mind. 1 MiB, bei "echten" Hostern bis zu 30 oder so.
LittleBen hat folgendes geschrieben : |
Und fallen am Server nicht noch größere Lasten an, wenn alles noch über eine PHP-Datei geht? |
PHP-Zugriffe sind das Normale, FTP-Zugriffe nicht. Deshalb ist es wohl eher umgekehrt, zumal das PHP-Skript gut optimieren kann.
LittleBen - Do 30.08.12 14:38
jaenicke hat folgendes geschrieben : |
Bei Delphi 7, ja. In aktuellen nicht soweit ich das sehe. ;-) |
Mal eine ganz doofe Frage: Wie kompilier ich denn die pas neu? Eine Änderung in der WinInet wird komischerweise einfach "übersehen" :gruebel:
jaenicke hat folgendes geschrieben : |
Nicht direkt. Ein Timeout wäre möglich, wenn es zu lange dauert, je nach Servereinstellung. Größere Datenmengen würde ich aber als Dateiupload hochladen, da gehen je nach Server mind. 1 MiB, bei "echten" Hostern bis zu 30 oder so. |
Ab welcher Größe würdest du "groß" sagen? Bei mir handelt es sich eig. nur um Mengen von 10 - 50 KB.
jaenicke hat folgendes geschrieben : |
PHP-Zugriffe sind das Normale, FTP-Zugriffe nicht. Deshalb ist es wohl eher umgekehrt, zumal das PHP-Skript gut optimieren kann. |
Ah okay, dachte es wäre umgekehrt :)
EDIT: Hab mich bei Ohost über die Append-Funktion informiert -> "ist und bleibt abgeschaltet"
jaenicke - Do 30.08.12 17:20
LittleBen hat folgendes geschrieben : |
jaenicke hat folgendes geschrieben : | Bei Delphi 7, ja. In aktuellen nicht soweit ich das sehe. ;-) | Mal eine ganz doofe Frage: Wie kompilier ich denn die pas neu? Eine Änderung in der WinInet wird komischerweise einfach "übersehen" :gruebel: |
Das ist nicht komisch, die .dcu ist ja noch da und die Quelltextdateien auch entsprechend konfiguriert (.pas ich Suchpfad, .dcu in Bibliothekspfad). Zum Neukompilieren kopierst du die am besten in deinen Projektordner und fügst die Unit dem Projekt hinzu. Empfehlenswert ist das aber ganz und gar nicht. Besser ist es nur selektiv die zu ändernde Funktion in deinem Projekt zu implementieren.
Sonst muss man das ja bei jeder Delphiversion erneut anpassen...
LittleBen hat folgendes geschrieben : |
Ab welcher Größe würdest du "groß" sagen? Bei mir handelt es sich eig. nur um Mengen von 10 - 50 KB. |
Bei den Datenmengen ist das im Grunde egal, aber wenn es sich nicht um reine Textdateien handeln sollte, is der Upload als Datei schon wegen der Zeichenkodierung sinnvoll.
LittleBen hat folgendes geschrieben : |
EDIT: Hab mich bei Ohost über die Append-Funktion informiert -> "ist und bleibt abgeschaltet" |
Freehoster halt ;-)
Ob sich das für die 2 Euro im Monat oder so lohnt, muss jeder selbst wissen. ;-)
LittleBen - Do 30.08.12 17:48
Stimmt! Es einfach nur im Projekt zu implementieren ist besser :)
Einen "richtigen" FTP-Server habe ich (bei Hosteurope), wollte ihn für dieses Projekt aber nicht benutzen (aus den Sicherheitsgründen, die du genannt hast). Hat sich jetzt aber geklärt.
Vielen, vielen Dank für deine Hilfe!
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!