Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2003.04.21;
Скачать: [xml.tar.bz2];

Вниз

Передача файла cgi скрипту по http   Найти похожие ветки 

 
Бес Имянный   (2003-02-26 20:31) [0]

Господа, появилась большая проблема.
Необходимо из Delphi приложения закачать файл на сервер, передав его скрипту также как это делает IE, обработывая форму типа

<input type="text" name="pic" size="30">
<input type="file" name="graphic" size="30">



Как правильно скомпоновать postdata?
Может компоненту какую посоветуете?


 
FND   (2003-02-27 00:23) [1]

Недавно сам мучался этой проблемой... Вот это должно помочь...

HTML File Upload
Оригинал статьи: Delphi Web Development
На этой страничке вы узнаете как закачать файл на веб-сервер, используя HTML.
Перед тем как приступить к разработке, желательно ознакомиться с RFC1867: Form-based File Upload in HTML, в котором описаны форматы передачи файлов в HTML.
Клиент
Чтобы закачать файл на сервер, Вам нужно использовать тэг FORM. Для передачи данных формы, обязательно, используется метод POST и устанавливается MIME media type: multipart/form-data.
Вы должны установить в тэг FORM атрибут ENCTYPE равным multipart/form-data, и в форму добавить элемент INPUT типа file.
Например, давайте создадим следующий HTML файл, назовем его upload.htm и положим его в корневой директорий веб сервера.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>DWD File Upload</title>
</head>

<body>
<form method="POST" action="/scripts/upload.dll" enctype="multipart/form-data">
<p><input type="checkbox" name="checkbox" value="ON">CheckBox Test
<p>RadioBox Test:
<input type="radio" value="ON" checked name="radiobox">ON
<input type="radio" value="OFF" name="radiobox">OFF
<p>TextBox Test:
<input type="text" name="text" size="20">
<p>File2 Test:
<input type="file" name="myfile1" size="20">
<p>File2 Test:
<input type="file" name="myfile2" size="20">
<p>File2 Test:
<input type="file" name="myfile3" size="20">
<p>
<input type="submit" value="Submit">
<input type="reset" value="Reset" name="B2">
</form>
</body>
</html>

Пример 1
Итак, мы создали форму которая содержит элементы checkbox, radio, text и три(!) элемента file.
Загрузите этот файл браузером, например, набирите http://localhost/upload.htm. Если, нажав кноку Browse на форме, выбрать файл 1: C:\image.gif, файл 2: C:\test.exe, файл 3: C:\text.txt и нажать кнопку Submit, в соответствии с RFC1867 браузер должен сформировать и передать в заголовке HTTP поле
ContentType: multipart/form-data; boundary=--7d015813802c4
и примерно следующий контент:
----7d015813802c4
Content-Disposition: form-data; name="checkbox"

ON
----7d015813802c4
Content-Disposition: form-data; name="radiobox"

ON
----7d015813802c4
Content-Disposition: form-data; name="text"

...текст из элемента текст...
----7d015813802c4
Content-Disposition: form-data; name="myfile1"; filename="C:\image.gif"
Content-Type: image/gif

...содержимое файла 1...
----7d015813802c4
Content-Disposition: form-data; name="myfile2"; filename="C:\test.exe"
Content-Type: application/octet-stream

...содержимое файла 2...
----7d015813802c4
Content-Disposition: form-data; name="myfile3"; filename="C:\text.txt"
Content-Type: text/plain

...содержимое файла 3...
----7d015813802c4--

Пример 2
Где boundary (граница) - разделитель полей.
Сервер
На стороне сервера, для приема данных, мы будем использовать ISAPI приложение, которое сейчас и создадим.
Запускаем Delphi, создаем с помощью Delphi IDE новый проект (мы создали ISAPI DLL, вы можете создать как ISAPI, так и CGI) и создаем обработчик WebActionItem. Установим его свойство Default равным True.
Приступим к написанию кода. Создадим обработчик события OnAction, для созданного WebActionItem. Прежде всего нужно быть уверенным, что принимаемые данные действительно имеют ContentType: multipart/form-data:

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
if (Pos("multipart/form-data", LowerCase(Request.ContentType)) > 0) and
(Pos("boundary", LowerCase(Request.ContentType)) > 0) then
begin

...

end
else
Response.Content := "Content-type is not "multipart/form-data".";
end;

Пример 3
Определим boundary, для этого воспользуемся функцией ExtractHeaderFields:
...
var
Boundary: string;
Header: TStrings;
begin
...
Header := TStringList.Create;
try
ExtractHeaderFields([";"], [" "], PChar(Request.ContentType), Header,
False, False);
Boundary := Header.Values["boundary"];
finally
Header.Free;
end;
...

Пример 4
Для того, чтобы прочитать переданные клиентом данные, нужно прочитать свойство Content объекта Request. Следует сказать, что Request.Content не обязательно должен содержать все данные т.к. размер свойства Content ограничен, особенно актуально это для передачи файлов большого размера. Для того чтобы прочитать следующую порцию данных, используйте метод ReadClient объекта Request. Метод ReadClient читает данные в буфер. В качесте буфера используем PChar. Определить размер данных, которые необходимо считать можно так:
AllContent := Request.Content;
ByteToRead := Request.ContentLength - Length(AllContent);
Далее следует зарезервировать память для буфера, для того чтобы экономнее расходовать память, ограничим максимальный размер буфера константой MaxReadBlockSize. Определим весь поток данных, переданный клиентом:
const
MaxReadBlockSize = 8192;

...


 
FND   (2003-02-27 00:29) [2]


var
Boundary, BufferStr, AllContent: string;
Header: TStrings;
ByteToRead, ReadedBytes, RSize: Integer;
Buffer: PChar;
begin
...
AllContent := Request.Content;
ByteToRead := Request.ContentLength - Length(AllContent);
try
while ByteToRead > 0 do
begin
RSize := MaxReadBlockSize;
if RSize > ByteToRead then
RSize := ByteToRead;
GetMem(Buffer, RSize);
try
ReadedBytes := Request.ReadClient(Buffer^, RSize);
SetString(BufferStr, Buffer, ReadedBytes);
AllContent := AllContent + BufferStr;
finally
FreeMem(Buffer, RSize);
end;
ByteToRead := Request.ContentLength - Length(AllContent);
end;
// и возвратим клиенту справочную информацию
Response.Content := Response.Content + Format("ContentType = %s<br>" +
"ContentLength = %d<br>Readed Bytes = %d<br>Boundary = %s<hr>",
[Request.ContentType, Request.ContentLength, Length(AllContent),
boundary]);

...

except
on E: Exception do
Response.Content := Response.Content + "<p>" + E.Message;
end;
...

Пример 5
Итог: мы прочитали весь переданный контент и присвоили его переменной AllContent, а также узнали разделитель Boundary.
Следующим этапом необходимо полученный контент разобрать по частям. Для этого напишем функцию
ReadMultipartRequest(const Boundary: string; ARequest: string; var AHeader: TStrings; var Data: string): string;

которая будет запрашивать разделитель Boundary и AllContent, и возвращать поля заголовка в AHeader и содержимое поля в Data. Результатом выполнения функции будет передан AllContent без первого поля формы. Например, после первой обработки данных, показанных в Примере 2, результат возвращаемый функцией будет:
----7d015813802c4
Content-Disposition: form-data; name="radiobox"

ON
----7d015813802c4
Content-Disposition: form-data; name="text"

...текст из элемента текст...
----7d015813802c4
Content-Disposition: form-data; name="myfile1"; filename="C:\image.gif"
Content-Type: image/gif

...содержимое файла 1...
----7d015813802c4
Content-Disposition: form-data; name="myfile2"; filename="C:\test.exe"
Content-Type: application/octet-stream

...содержимое файла 2...
----7d015813802c4
Content-Disposition: form-data; name="myfile3"; filename="C:\text.txt"
Content-Type: text/plain

...содержимое файла 3...
----7d015813802c4--

Пример 6
объект AHeader будет содержать:
Content-Disposition=form-data; name="checkbox" а переменная Data = "ON". После четвертой обработки будет:
----7d015813802c4
Content-Disposition: form-data; name="myfile2"; filename="C:\test.exe"
Content-Type: application/octet-stream

...содержимое файла 2...
----7d015813802c4
Content-Disposition: form-data; name="myfile3"; filename="C:\text.txt"
Content-Type: text/plain

...содержимое файла 3...
----7d015813802c4--



 
FND   (2003-02-27 00:31) [3]


Пример 7
объект AHeader будет содержать: Content-Disposition=form-data; name="myfile1"; filename="C:\image.gif" Content-Type=image/gif а переменная Data = ...содержимое файла 1....
Код функции приведен в Примере 8:
function TWebModule1.ReadMultipartRequest(const Boundary: string;
ARequest: string; var AHeader: TStrings; var Data: string): string;
var
Req, RHead: string;
i: Integer;
begin
Result := "";
AHeader.Clear;
Data := "";
if (Pos(Boundary, ARequest) < Pos (Boundary + "--", ARequest))
and (Pos(Boundary, ARequest) = 1) then
begin
Delete(ARequest, 1, Length(Boundary) + 2);
Req := Copy(ARequest, 1, Pos(Boundary, ARequest) - 3);
Delete(ARequest, 1, Length(Req) + 2);
RHead := Copy(Req, 1, Pos(#13#10#13#10, Req)-1);
Delete(Req, 1, Length(RHead) + 4);
AHeader.Text := RHead;
for i := 0 to AHeader.Count - 1 do
if Pos(":", AHeader.Strings[i]) > 0 then
AHeader.Strings[i] := Trim(Copy(AHeader.Strings[i], 1,
Pos(":", AHeader.Strings[i])-1)) + "=" + Trim(Copy(AHeader.Strings[i],
Pos(":", AHeader.Strings[i])+1, Length(AHeader.Strings[i]) -
Pos(":", AHeader.Strings[i])));
Data := Req;
Result := ARequest;
end
end;

Пример 8
Теперь осталось только вызвать данную процедуру в цикле и сохранить данные на диск.
Определим константу UploadPath, которая будет содержать путь к папке, в которую будем сохранять принятые файлы.
const
UploadPath = "C:\temp\upload\";

...

var
AllContent, Boundary, Data: string;
Header, HList: TStrings;
OutStream: TFileStream;
begin
...
if Request.ContentLength = Length(AllContent) then
while Length(AllContent) > Length("--" + Boundary + "--" + #13#10) do
begin
Header := TStringList.Create;
HList := TStringList.Create;
try
AllContent := ReadMultipartRequest("--" + Boundary, AllContent,
Header, Data);
ExtractHeaderFields([";"], [" "],
PChar(Header.Values["Content-Disposition"]), HList, False, True);
if (Header.Values["Content-Type"] <> "") and (Data <> "") then
begin
OutStream:=TFileStream.Create(UploadPath +
ExtractFileName(HList.Values["filename"]), fmCreate);
try
try
OutStream.WriteBuffer(Pointer(Data)^, Length(Data));
Response.Content := Response.Content +
Format("<p>File %s saved.",
[ExtractFileName(HList.Values["filename"])]);
except
Response.Content := Response.Content +
Format("<p>Unable to save file %s.", [UploadPath +
ExtractFileName(HList.Values["filename"])]);
end;
finally
OutStream.Free;
end
end
else
Response.Content := Response.Content + Format("<p>Field %s = %s",
[HList.Values["name"], Data]);
finally
Header.Free;
HList.Free;
end;
end;
...



Пример 9
В результате выполнения данного блока программы, поля, которые содержат файл, будут сохранены на диск в папку заданную константой UploadPath, а поля, не содержащие файл будут показаны в результате выполнения скрипта, имя поля и значение будут переданы клиенту.



 
FND   (2003-02-27 00:38) [4]

В общем мне помогло... Нашел совершенно случайно поисковиком
aport.
Теперь там уже нет...
Желаю удачи...


 
Бес Имянный   (2003-02-27 14:41) [5]

Большое спасибо за подробный ответ. С проблемой получения файлов CGI скриптом я месяца 2 назад мучался. Средствами Delphi не решил - воспользовался php.

Сейчас у меня обратная проблема - мне нужно сформировать post запрос для обращения к серверу.


 
Бес Имянный   (2003-02-27 14:42) [6]

Вроде бы проблема решена.
Посоветовали следующее...

const
LR : String[2] = #13#10;

procedure PrepareAndRunCGI(CGIName: string; FN, FNL: string);
begin
Http.URL := CGIURL+CGIName; {FIX ME: Must read settings from fSetup dialog }
Boundry := "-----------------------------7cf29e12c04"; { Specified in Multipart/form-data RFC }
Http.ContentTypePost := "multipart/form-data; boundary=" + copy(Boundry,3,length(boundry));
Buf := Boundry + LR + "Content-Disposition: form-data; name="upfile"; filename=""+
FN+"""+LR + "Content-Type: image/pjpeg" + LR + LR;

//i don"t think the IMAGE/PJPEG matters much
//my opinion on this would be that once it"s set as this
//the file will be uploaded in BINARY

Http.SendStream.Write(Buf[1], Length(Buf));

FileToSend := TMemoryStream.Create;
// Open File with TMemorystream
if FileExists(FNL) then FileToSend.LoadFromFile(FNL);
// load it up :)
FileToSend.SaveToStream(Http.SendStream);
// Dump out file to TMemorystream
FileToSend.Free;
// andddd free it up :)
Buf := InputText("variable",intToStr(FirmID));
Buf := ConCat(buf,Boundry+"--"); {This is to signify the END of our transactions }
// Log(Buf);
Http.SendStream.Write(Buf[1], Length(Buf));
Http.SendStream.Seek(0, soFromBeginning);
Http.Post;
end;



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2003.04.21;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.5 MB
Время: 0.01 c
3-67092
Stas
2003-04-02 12:34
2003.04.21
Не допустимая закладка


9-67081
Ketmar
2002-11-08 18:31
2003.04.21
OpenGL: определение того, был ил отрисован хоть один пиксел...


14-67497
VaS
2003-04-03 11:40
2003.04.21
Работа в Москве


1-67301
ferrik
2003-04-11 18:33
2003.04.21
Обращение к компонентам


14-67501
Sheng
2003-04-03 17:27
2003.04.21
TreeView





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский