Autor |
Beitrag |
LittleBen
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: So 26.08.12 15:44
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
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 26.08.12 16:14
Siehe Dokumentation: msdn.microsoft.com/e...384170(v=vs.85).aspx hat folgendes geschrieben: | FtpPutFile is a high-level routine that handles all the bookkeeping and overhead associated with reading a file locally and storing it on an FTP server. An application that needs to send file data only, or that requires close control over the file transfer, should use the FtpOpenFile and InternetWriteFile functions. |
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.
Für diesen Beitrag haben gedankt: LittleBen
|
|
LittleBen
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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
Beiträge: 632
Erhaltene Danke: 121
Win 7 32-bit
Delphi 2006/XE
|
Verfasst: 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
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 334
Erhaltene Danke: 3
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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
Beiträge: 334
Erhaltene Danke: 3
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: Mo 27.08.12 14:17
Ok, ein Fehler bekomm ich nun nicht mehr, aber es wird komischerweise nichts ausgelesen?
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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: Mi 29.08.12 13:55
Hier nun die funktionierende Funktion:
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: support.microsoft.com/kb/182316/de
|
|
jaenicke
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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:
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
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 29.08.12 22:31
Du musst dies am FTP-Server konfigurieren. Standardmäßig ist das oft deaktiviert.
|
|
LittleBen
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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. | 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. | 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. | www.nsftools.com/tips/RawFTP.htm#PASV
|
|
jaenicke
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 30.08.12 09:36
|
|
LittleBen
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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
Beiträge: 19277
Erhaltene Danke: 1741
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 30.08.12 12:26
|
|
|