Upload plików, multipart/form-data

0

Mam sobie takie coś:

<form method="post" action="index.php" enctype="multipart/form-data">
<input type="text" name="input1">
<input type="text" name="input2">
<input type="file" name="input3">
<input type="submit" name="input4">
</form>
  • Jakich klas trzeba użyć, aby wysłać plik przy pomocy takiego formularza?
    (Podejrzewam że będą to HttpWebRequest oraz HttpWebResponse, jeszcze jakieś?)
  • Co należy zrobić, aby monitorować proces uploadu pliku, czyli jak stwierdzić np, że
    w danym momecie wysłało się tyle a tyle KB?

Bardzo dziękuję za odp!

0
Roland napisał(a)
  • Jakich klas trzeba użyć, aby wysłać plik przy pomocy takiego formularza?
    (Podejrzewam że będą to HttpWebRequest oraz HttpWebResponse, jeszcze jakieś?)

Dobrze podejrzewasz. Bedziesz musial sam zbudowac wiadomosc w formacie multipart, ale nie jest to jakas wielka filozofia.

Roland napisał(a)
  • Co należy zrobić, aby monitorować proces uploadu pliku, czyli jak stwierdzić np, że
    w danym momecie wysłało się tyle a tyle KB?

HttpWebRequest normalnie uzywa sie wywolujac metode GetResponse. Ale jest tez wersja asynchroniczna, wiec moze z jej pomoca bedziesz mogl monitorowac przebieg, ale nie jestem pewien.

pozdrawiam
johny

0

Słuchajcie, a więc zbudowałem coś takiego, co ma służyć do uploadu plików:

                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
                byte[] bdata = ASCIIEncoding.ASCII.GetBytes(data);
                req.ContentLength = bdata.Length;
                req.ContentType = "multipart/form-data";
                req.Method = "POST";
                req.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)";
                Stream reqStream = req.GetRequestStream();
                reqStream.Write(bdata, 0, bdata.Length);
                reqStream.Close();
                HttpWebResponse res = (HttpWebResponse)req.GetResponse();
                Stream resStream = res.GetResponseStream();
                StreamReader reader = new StreamReader(resStream, System.Text.Encoding.ASCII);
                data = reader.ReadToEnd();

A oto dane z formularza:

string url = "http://nazwajakas.pl/index.php";
string data = "input1=jakistekst" +
                    "&input2=jakasnazwa2" +
                    "&input3=C:\\plik.txt"; // a tu ścieżka do pliku

Pytanie jest takie: gdzie popełniłem błąd(bo powyższy kod nie działa...)
Dziękuję za odpowiedź!

0

Zle mnie zrozumiales. 'Bedziesz musial sam zbudowac wiadomosc w formacie multipart' oznaczalo cos takiego:

	HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(URL);
	String Boundary = Guid.NewGuid().ToString().Replace("-","");
	Request.Method = "POST";
	Request.ContentType =  "multipart/form-data; boundary=" + Boundary;
	MemoryStream PostData = new MemoryStream();
	String newline = "\r\n";
	StreamWriter StreamWriter = new StreamWriter(vPostData);
	StreamWriter.Write("--" + Boundary + NewLine);
	StreamWriter.Write("Content-Disposition: form-data;name=\"{0}\";filename=\"{1}\"{2}",filename, filename, newline);
	StreamWriter.Write("Content-Type: plain/text " + newline +newline);
	StreamWriter.Flush();
	PostData.Write(filecontent, 0, filecontent.Length);
	StreamWriter.Write(newline);
	StreamWriter.Write("--{0}--{1}", Boundary,newline);
	StreamWriter.Flush();
	Request.ContentLength = PostData.Length;
	Stream Stream = Request.GetRequestStream();
        PostData.WriteTo(Stream);
	PostData.Close(); 
	WebResponse Response = Request.GetResponse();

czyli zgodnie ze specyfikacja RFC - nie pamietam ktorego :)

Zreszta podgladnij sobie snifferem to co wysylasz do serwera z html'a to zobaczysz o bo chodzi.

pozdrawiam
johny

0

Nie bardzo rozumiem...
Po pierwsze jest mi obce pojęcie boundary. Co to jest?
Po drugie mam rozumieć, że vPostData to w moim przykładzie będzie:

"input1=jakistekst" +
"&input2=jakasnazwa2" +
"&input3=C:\\plik.txt"; // a tu ścieżka do pliku

?

// Dzięki za odp

0

Poczytaj sobie jak wyglada format multipart/form-data.

To co dalem powyzej to poskladanie w jedna wiadomosc wszystkiego co potrzebne, zeby php odczytal to co dostal jako wiadomosc tego typu i wiedzial, ze to plik. Stworz sobie formularz w html, wyslij gdziekolwiek i podsluchaj snifferem co wychodzi.

Boundary jest po to, zeby oddzielic czesci wiadomosci od siebie - naglowek, tresc, itd. Musi oczywiscie sie roznic od kazdego innego ciagu znakow w wiadomosci - dlatego tutaj generowany jest losowy.

PostData zawiera wszystko to, co jest wysylane jako format multipart. Podsluchaj snifferem swoj formularz, pozniej to, a zrozumiesz.

pozdrawiam
johny

0

:|
Zacznijmy od początku, koncentrując się na moim przykładzie(który znajdziecie u góry);
A więc tak:

1.) Tworzymy nowy obiekt klasy HttpWebRequest oraz ustawiamy w nim potrzebne właściwości

// Było action="index.php" a więc url = http://serwer.com/index.php
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://serwer.com/index.php");
req.Method = "POST";
req.Accept = @"text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg,
                      image/gif, image/x-xbitmap, */*;q=0.1";
req.Referer = "http://mail.walla.com"; // Czyżby był to link zwrotny, czyli returnurl ??
req.ContentType = @"multipart/form-data;
                boundary=---------------------------7d5158213e047e"; // czy tam boundary może wyglądać?
req.UserAgent = @"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)";
req.KeepAlive = true;
req.Headers.Add("Pragma", "no-cache");
req.Headers.Add("Accept-Languag", "en-us");
 req.Timeout = Timeout.Infinite;

2.) krokiem drugim będzie utworzenie zmiennej payload:

string payload =
"---------------------------7d5158213e047e\r\n" +
"Content-Disposition:form-data; input1=\"wartosc_inputa1\"\r\n\"" +
"\r\n" +
"---------------------------7d5158213e047e\r\n" +
"Content-Disposition:form-data; input2=\"wartosc_inputa2\"\r\n\"" +
"\r\n" +
"---------------------------7d5158213e047e\r\n" +
"Content-Disposition:form-data; input3=\"sciezka_do_pliku\"\r\n\"" +
"\r\n" +
"Content-Type: " + "plain/text" + "\r\n" + // czy tu aby napewno ma być plain/text jak przesyłam plik?
"\r\n";

FileInfo file = new FileInfo("C:\\plik.txt");
int filesize = Convert.ToInt32(file.Length); // waga naszego pliku
req.ContentLength = payload.Length + file.Length; // i wszystko razem

3.) Utworzenie strumienia z Requesta i wpisanie do niego payload i file:

                Stream conn = req.GetRequestStream();
                byte[] body_header = System.Text.Encoding.ASCII.GetBytes(payload);
                conn.Write(body_header, 0, body_header.Length);
                FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
                byte[] file_content = new byte[filesize];
                int len = fs.Read(file_content, 0, filesize);
                conn.Write(file_content, 0, len);
                fs.Close();

                Stream respon = req.GetResponse().GetResponseStream();
                StreamReader sr = new StreamReader(respon);
                sr.Close();
                respon.Close();
                conn.Close();

Gdzie popełniłem błąd?
W którym kroku?

A wracając do sniffera:
Oto log:

POST / HTTP/1.1
User-Agent: Opera/9.00 (Windows NT 5.0; U; en)
Host: <tu host>
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: pl,en;q=0.9
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
0

Jak na moj gust strasznie nagmatwany ten naglowek, do tego nie zamykasz wiadomosci - po zawartosci pliku musi byc jeszcze nowa linia oraz --boundary-- i znowu nowa linia.

Jesli chodzi o sniffer to podsluchaj to co jest wysylane w momencie wyslania formularza, a nie otworzenia strony html (z tego co widze). Wyslij sobie jakis maly plik txt i zobacz co zostanie wyslane w pakiecie - powinna byc cala wiadomosc lacznie z zawartoscia pliku.

pozdrawiam
johny

0

OK:

string boundary = "------------dJgQ3fn7rgGFtImttEUndP";
string payload =
                    //
                    boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"input1\"\r\n" +
                    "Content-Type: text/plain\r\n" +
                    "\r\n" +
                    "tu_wartość_inputa1\r\n" +
                    //
                    boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"input2\"\r\n" +
                    "Content-Type: text/plain\r\n" +
                    "\r\n" +
                    "tu_wartość_inputa2\r\n" +
                    //
                    boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"input3\"; filename=\"test.txt"\r\n" +
                    "Content-Type: text/plain\r\n" +
                    "\r\n" +
                    "co_ma_być_tutaj?\r\n" +
                    //
                    boundary + "--\r\n";

A więc tak to (moim zdaniem) powinno wyglądać.
Pytania:
Wysyłając plik test.txt w snifferze było tak:

                    Content-Disposition:form-data; name="input3"; filename="plik.txt"
                    Content-Type: text/plain"
 
                     Test
                    ------------dJgQ3fn7rgGFtImttEUndP--

"Test" < to wyraz w pliku plik.txt

W pole "co_ma_być_tutaj?" wstawiłem jakąś linikę tekstu ale ten sposób nie działa.
Co mam zrobić, aby to wreszcie zadziałało?

0

Powiem szczerze, ze sie pogubilem w tym twoim. Jak uzywasz mojego to jak dziala?

pozdrawiam
johny

0

Zapytam inaczej:
tworzę sobie payload czyli string wiadomości.
Później tworzę strumień z Requesta i do tego strumienia wpisuje payload, tak?

0
Roland napisał(a)

Zapytam inaczej:
tworzę sobie payload czyli string wiadomości.
Później tworzę strumień z Requesta i do tego strumienia wpisuje payload, tak?

Tak.

pozdrawiam
johny

0

To znowu ja.. Jestem bardzo blisko rozwiązania problemu, ale jeszcze go nie rozwiązałem
Więc:

Uruchamiam przeglądarkę oraz sniffera.
Zamierzam wysłać plik o nazwie plik.txt .
W pliku plik.txt znajduje się tekst : "Mam problem".
Oto, co ujżałem w moim "wąchaczu", gdy chciałem "upnąć" (wysłać) plik plik.txt z przeglądarki
(fragment - końcówka):

-----------------------------635224930793328
Content-Disposition: form-data; name="plik.txt"
Content-type: application/octet-stream

Mam problem
-----------------------------635224930793328--

A więc buduje sobie taki payload (końcówka):

                    "--" + boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"file\"; filename=\"plik.txt\"\r\n" +
                    "Content-type: application/octet-stream\r\n" +
                    "Mam problem\r\n" +
                    "--" + boundary + "--\r\n";

A następnie wrzucam to do strumienia:

                req.ContentLength = payload.Length;
                streamWriter = req.GetRequestStream();
                body_header = System.Text.Encoding.UTF8.GetBytes(payload);
                streamWriter.Write(body_header, 0, body_header.Length);

Niestety, jak dobrze się domyślacie plik się nie uploaduje, dlaczego?
Czy kod jest dobrze napisany, czy tak się prawidłowo powinno pisac taki kod, czy może najpierw wysyłamy cały payload, a dopiero później tekst "Mam problem"?

0

Zauwaz, ze przed trescia pliku jest jeszcze jedna nowa linia.

pozdrawiam
johny

0

Tak, to fakt, popełniłem błąd, ale to nie zmienia faktu, że plik dalej się nie chce wysłać...

0

Daj caly kod tego wysylania, porownam u siebie.

pozdrawiam
johny

0

A więc pracuje nad programem do wysyłania maili z Http przy pomocy konta Interia.pl .
Program loguje się, następnie wchodzi na stronę "http://poczta.interia.pl/poczta/newmessage/".
I teraz następuje wysłanie załącznika.
Oto log ze sniffera - połączenie przez przeglądarkę:

POST /popup/attachments/mid.html HTTP/1.1
Referer: http://poczta.interia.pl/poczta/newmessage/
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040626 Firefox/0.8
Content-Type: multipart/form-data; boundary=---------------------------565507094554772
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: pl,en-us;q=0.7,en;q=0.3
Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7
Host: poczta.interia.pl
Cookie: C_IDNT=44f72d2f58dc23.84028323; VAST=853504429edb2bc2b63c81340768ba1a; IPLWWWID=<mój login>%26%230064%3Binteria.pl; EVAST=1157052386; __ads_b64_INTERIAPL=<fHdvamV3b2R6dHdvI2RvbG5vc2xhc2tpZXw=>
Content-Length: 732
Expect: 100-continue

-----------------------------565507094554772
Content-Disposition: form-data; name="attachment1"; filename="pliczek"
Content-type: application/octet-stream

jestemwpliku
-----------------------------565507094554772
Content-Disposition: form-data; name="attachment2"


-----------------------------565507094554772
Content-Disposition: form-data; name="attachment3"


-----------------------------565507094554772
Content-Disposition: form-data; name="attachment4"


-----------------------------565507094554772
Content-Disposition: form-data; name="attachment5"


-----------------------------565507094554772
Content-Disposition: form-data; name="attachment6"


-----------------------------565507094554772--

A więc buduje taki kod:

                HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://poczta.interia.pl/popup/attachments/mid.html");
                req.CookieContainer = new CookieContainer();
                req.CookieContainer.Add(cookies);
                req.Method = "POST";
                req.Accept = @"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
                req.Referer = "http://poczta.interia.pl/poczta/newmessage/";
                req.ContentType = @"multipart/form-data; boundary=" + boundary;
                req.UserAgent = @"Opera/9.00 (Windows NT 5.0; U; en)";
                req.KeepAlive = true;
                req.Headers.Add("Accept-Language", "pl,en;q=0.9");
                req.Timeout = Timeout.Infinite;

                Stream streamWriter;
                byte[] body_header;

                string payload =
                    //
                    "--" + boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"attachment1\"; filename=\"pliczek.txt\"\r\n" +
                    "Content-type: application/octet-stream\r\n" +
                    "\r\n" +
                    "jestemwpliku\r\n" +
                    //
                    "--" + boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"attachment2\"\r\n" +
                    "\r\n" +
                    "\r\n" +
                    //
                    "--" + boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"attachment3\"\r\n" +
                    "\r\n" +
                    "\r\n" +
                    //
                    "--" + boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"attachment4\"\r\n" +
                    "\r\n" +
                    "\r\n" +
                    //
                    "--" + boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"attachment5\"\r\n" +
                    "\r\n" +
                    "\r\n" +
                    //
                    "--" + boundary + "\r\n" +
                    "Content-Disposition:form-data; name=\"attachment6\"\r\n" +
                    "\r\n" +
                    "\r\n" +
                    //
                    "--" + boundary + "--\r\n";

                req.ContentLength = payload.Length;
                streamWriter = req.GetRequestStream();
                body_header = System.Text.Encoding.UTF8.GetBytes(payload);
                streamWriter.Write(body_header, 0, body_header.Length);
                streamWriter.Close();

                HttpWebResponse res = (HttpWebResponse)req.GetResponse();
                Stream resStream = res.GetResponseStream();
                StreamReader reader = new StreamReader(resStream, Encoding.ASCII);
                data = reader.ReadToEnd();
                res.Cookies = req.CookieContainer.GetCookies(req.RequestUri);
                cookies = res.Cookies;

Zmienna data przechowuje odpowiedź serwera. Gdy łącze się przez przeglądarkę, to ta odpowiedź to źródło strony, a wniej jakiś tekst typu "Plik został załączony do wiadmości" i takie tam inne.

Niestety, kiedy do akcji wkracza mój program to odpowiedź sererwa czyli zmienna data ma wartość null.

Bardzo proszę o pomoc i dziękuję z zanią z góry!

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