[Indy] Przesyłanie plików przez HTTP

0

Po wyjaśnieniu powodu usunięcia mojego topica z tego działu zamieszczam go tu raz jeszcze:
Mam takie pytanko. Dla kogoś, kto zajmował się Indy pewnie banalnie proste. Potrzebuję stworzyć program, który będzie miał moźliwość wysyłania plików na serwer za pomocą protokołu HTTP, tak jak to robi przeglądarka przy wysyłaniu formularza z polem typu File. No i teraz powstaje pytanie, czy Indy ma w komponencie IdHTTP (albo innym, ale wydaje mi się, źe raczej byłoby to nielogiczne :]) jakoś zaimplementowanie wysyłanie danych tak jak wysyłanie danych z formularza, źebym podał wszystkie pola i ich wartości i nazwy plików w plach typu file i źeby mi to wszystko wysłał, czy muszę się bawić czystym ClientSocketem i tam kombinować z tworzeniem odpowiedniego zapytania? Jeśli musiałbym skorzystać z ClientSocketa, to jest ktoś mi w stanie wytłumaczyć skąd się biorą te bliźej niezidentyfikowane przeze mnie liczby, które rozpoczynają i kończą blok z plikiem?
//Po raz drugi w karierze musiałem coś wyciągać z Newbie, źeby uzyskać odpowiedż :P. Moźe powinienem obniźać poprzeczkę, do której coś daję do Newbie :P

0

Cześć
Przesyłam Ci dwa listingi

  1. Unit który deklarujesz w głownym unicie;
unit MsMultiPartFormData;

interface

uses
  SysUtils, Classes;

const
  CONTENT_TYPE = 'multipart/form-data; boundary=';
  CRLF = #13#10;
  CONTENT_DISPOSITION = 'Content-Disposition: form-data; name="%s"';
  FILE_NAME_PLACE_HOLDER = '; filename="%s"';
  CONTENT_TYPE_PLACE_HOLDER = 'Content-Type: %s' + crlf + crlf;
  CONTENT_LENGTH = 'Content-Length: %d' + crlf;

type
  TMsMultiPartFormDataStream = class(TMemoryStream)
  private
    FBoundary: string;
    FRequestContentType: string;
    FInitial: Boolean;
    function GenerateUniqueBoundary: string;
  public
    procedure AddFormField(const FieldName, FieldValue: string);
    procedure AddFile(const FieldName, FileName, ContentType: string; FileData: TStream); overload;
    procedure AddFile(const FieldName, FileName, ContentType: string); overload;
    procedure PrepareStreamForDispatch;
    constructor Create;
    property Boundary: string read FBoundary;
    property RequestContentType: string read FRequestContentType;
  end;

implementation

{ TMsMultiPartFormDataStream }

constructor TMsMultiPartFormDataStream.Create;
begin
  inherited;
  FInitial := True;
  FBoundary := GenerateUniqueBoundary;
  FRequestContentType := CONTENT_TYPE + FBoundary;
end;

procedure TMsMultiPartFormDataStream.AddFile(const FieldName, FileName,
  ContentType: string; FileData: TStream);
var
  sFormFieldInfo: string;
  Buffer: PChar;
  iSize: Int64;
begin
  iSize := FileData.Size;
// Malikyar -- Removed the Content_length parameter since the web buffer did not contain it.
{
  sFormFieldInfo := Format(CRLF + '--' + Boundary + CRLF + CONTENT_DISPOSITION +
    FILE_NAME_PLACE_HOLDER + CRLF + CONTENT_LENGTH +
      CONTENT_TYPE_PLACE_HOLDER, [FieldName, FileName, iSize, ContentType]);
}
  sFormFieldInfo := Format(CRLF + '--' + Boundary + CRLF + CONTENT_DISPOSITION +
    FILE_NAME_PLACE_HOLDER + CRLF +
      CONTENT_TYPE_PLACE_HOLDER, [FieldName, FileName, ContentType]);

  Write(Pointer(sFormFieldInfo)^, Length(sFormFieldInfo));
  FileData.Position := 0;
  GetMem(Buffer, iSize);
  try
    FileData.Read(Buffer^, iSize);
    Write(Buffer^, iSize);
  finally
    FreeMem(Buffer, iSize);
  end;
end;

procedure TMsMultiPartFormDataStream.AddFile(const FieldName, FileName,
  ContentType: string);
var
  FileStream: TFileStream;
begin
  FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    AddFile(FieldName, FileName, ContentType, FileStream);
  finally
    FileStream.Free;
  end;
end;

procedure TMsMultiPartFormDataStream.AddFormField(const FieldName,
  FieldValue: string);
var
  sFormFieldInfo: string;
begin
// Add a check to see if it's the initial field being added.  If so, then do not preface with a CRLF.
   if FInitial then
   begin
     sFormFieldInfo :=
       Format('--' + Boundary + CRLF + CONTENT_DISPOSITION + CRLF + CRLF +
         FieldValue, [FieldName]);
     FInitial := False;
   end
   else
     sFormFieldInfo :=
       Format(CRLF + '--' + Boundary + CRLF + CONTENT_DISPOSITION + CRLF + CRLF +
         FieldValue, [FieldName]);
  Write(Pointer(sFormFieldInfo)^, Length(sFormFieldInfo));
end;

function TMsMultiPartFormDataStream.GenerateUniqueBoundary: string;
begin
  Result := '---------------------------' + FormatDateTime('mmddyyhhnnsszzz', Now);
end;

procedure TMsMultiPartFormDataStream.PrepareStreamForDispatch;
var
  sFormFieldInfo: string;
begin
  sFormFieldInfo := CRLF + '--' + Boundary + '--' + CRLF;
  Write(Pointer(sFormFieldInfo)^, Length(sFormFieldInfo));
  Position := 0;
  FInitial := True;
end;

end.
  1. unit główny programu wedłóg którego się wzoruj kładąc komponenty takie jakie
    są tu wymienione - skompiluj i zobacz ;)
    chyba o to chodzi bo to jest upload pliku na serwer
    <delphi>
    unit UploadForm;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
IdHTTP;

type
TForm1 = class(TForm)
IdHTTP1: TIdHTTP;
Label1: TLabel;
edtPersonName: TEdit;
Label2: TLabel;
edtMIMEType: TEdit;
Label3: TLabel;
edtDescription: TEdit;
Label4: TLabel;
edtFile: TEdit;
Button1: TButton;
OpenDialog1: TOpenDialog;
Button2: TButton;
Label5: TLabel;
edtHost: TEdit;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

uses
MsMultiPartFormData;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
edtFile.Text := OpenDialog1.FileName;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
ResponseStream: TMemoryStream;
MultiPartFormDataStream: TMsMultiPartFormDataStream;
begin
MultiPartFormDataStream := TMsMultiPartFormDataStream.Create;
ResponseStream := TMemoryStream.Create;
try
IdHttp1.Request.ContentType := MultiPartFormDataStream.RequestContentType;
MultiPartFormDataStream.AddFormField('PersonName', edtPersonName.Text);
MultiPartFormDataStream.AddFormField('Description', edtDescription.Text);
MultiPartFormDataStream.AddFile(edtFile.Name, edtFile.Text, edtMIMEType.Text);
{ You must make sure you call this method before sending the stream }
MultiPartFormDataStream.PrepareStreamForDispatch;
MultiPartFormDataStream.Position := 0;
IdHTTP1.Post(edtHost.Text, MultiPartFormDataStream, ResponseStream);
finally
MultiPartFormDataStream.Free;
ResponseStream.Free;
end;
end;

end.
<delphi>

0

Hmmm... Próbowałem obydwu metod, ale niestety: dostaję komunikat EException z takim komentarzem:

The server side of this connection has disconnected normaly but your client has attempted to read or write to the connection. You should trap this error using a try..except. Please see the help file for possible further information.

No i powstaje pytanie, co zrobić w takiej sytuacji? Co mogę robić źle? Wygląda na to, że klient w ogóle się nie łączy, znaczy łączy się, ale przed przesłaniem czegokolwiek zrywa połączenie :(.

0

Hej

Na mój upload działa.. przed chwilą sprawdziłem
w z tej uwagi co Ci wyświetla, wynika że połączenie zerwane ponieważ serwer nie udostępnia mozliwości odebrania pliku ...(tak wynika z tłumaczenia ang)

0

No tak, ale jak bezpośrednio ze stronki na tym serwerze robię upload, to działa, a przez ten programik nie :/. Chyba, że coś źle podaję... Jakie powinne być przykładowe wartości tych editów? Interesuje mnie głównie typ mime...

0

A więc pierwszy edit
"File Name at Destination" wpisujesz nazwę pliku jaka będzie widoczna w dziale upload Twojej stronki przykładowo.
"Destination Folder" - nazwa folderu na serwerze do którego wysyłasz plik
"MIME type" określa typ pliku , więc zostaw mu wpis application/msword , dla wszystkich plików
"File" wpisujesz ścieźkę dostępu do swojego pliku na swoim kompie
"Host" pełny link do serwera z uwzględnieniem skryptu odpowiedzialnego za przyjmowanie plików na serwer, masz tam przykładowo: http://www.matlus.com/scripts/multifileupload.dll/Upload

o to chodzi ?

0

Mam coś takiego:

procedure TForm1.Button1Click(Sender: TObject);
var
  ResponseStream: TMemoryStream;
  MultiPartFormDataStream: TMsMultiPartFormDataStream;
begin
  MultiPartFormDataStream := TMsMultiPartFormDataStream.Create;
  ResponseStream := TMemoryStream.Create;
  try
    MultiPartFormDataStream.AddFormField('PersonName', 'Ja');
    MultiPartFormDataStream.AddFormField('Description', 'Opis');
    MultiPartFormDataStream.AddFile('pliczek', 'D:\Documents and Settings\Adam\Moje Dokumenty\imiona.txt', 'application/msword');
    { You must make sure you call this method *before* sending the stream }
    MultiPartFormDataStream.PrepareStreamForDispatch;
    MultiPartFormDataStream.Position := 0;
    Http.Request.ContentType := MultiPartFormDataStream.RequestContentType;
    HTTP.Post('http://localhost/cvs/tmpfileinterpreter.php', MultiPartFormDataStream, ResponseStream);
  finally
    MultiPartFormDataStream.Free;
    ResponseStream.Free;
  end;
end;

I to nie chce działać :/ A jak to wywołuję z formularza www, to działa :/

0

ja bym obstawiał typ MIME application/octet-stream jako najbardziej uniwersalny dla każdego typu danych... może to coś pomoże?

0

No, niestety dalej jest źle :/
Ku mojemu zdziwieniu po przeprowadzeniu paru testów zauważyłem, że to wysyła wszystko jak powinno, ale mimo to nie działa. I to wbrew tej informacji Klient zamyka połączenie. Bo jak po drodze jest mój "proxy", który samemu w żadnej sytuacji sam połączenia nie zrywa, to mimo to dalej nie działa :(

0

Zapraszam na Forum o TWebBrowser http://4programmers.net/Forum/viewtopic.php/id=64871

0

Ech, trudno, w końcu zrobiłem to na czystych socketach. A do bizon'a: Po pierwsze - nie robię przeglądarki (już są takie przeglądarki, jakich się raczej nie wyprzedzi, a TWebBrowser - no ba, nie chcę Cię zniechęcać albo coś, ale na tym się nie oprze dobrej przeglądarki), po drugie - potrzebuję tego na trochę niższym poziomie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1