I am trying to POST some data as if I were using FORM on an HTML site (ContentType = multipart / form-data). The goal is Amazon s3. I use HttpWebRequest / HttpWebResponse and it all seems fine to me, but I still can’t deal with one problem, I keep getting the error:
<?xml version="1.0" encoding="UTF-8"?> <Error> <Code>InvalidArgument</Code> <Message>POST requires exactly one file upload per request.</Message> <ArgumentValue>0</ArgumentValue> <ArgumentName>file</ArgumentName> </Error>
seems self-describing, but I'm really sending the file field. I was able to send the file without problems through the test website, all the post data seems to be exactly the same. When I "spied" the request through wirehark all the headers and mail data, and also - it is there, as expected.
Does anyone have any ideas why amazon cannot see the file field?
Code used
(it's shortened a bit to make it more readable):
private void addStringToStream(string buff, Stream s) { var bytes = Encoding.UTF8.GetBytes(buff); s.Write(bytes, 0, bytes.Length); counter += bytes.Length; } private void doPost() { HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); request.Method = "POST"; request.ProtocolVersion = new Version(1, 1); request.KeepAlive = true; request.AllowAutoRedirect = true; request.Headers.Add("Accept-Language", "en-us,en;q=0.5"); request.Headers.Add("Accept-Encoding", "gzip,deflate"); request.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); request.Accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.10) Gecko/20071115 Firefox/2.0.0.10"; string boundary = "---------------------------317205771417341028"; request.ContentType = "multipart/form-data; boundary=" + boundary; request.SendChunked = false; request.Credentials = CredentialCache.DefaultCredentials; request.CookieContainer = new CookieContainer(); request.ContentLength = calculateLength(); using (Stream s = request.GetRequestStream()) { boundary += "\n"; string inLineContentH = "Content-Disposition: form-data; name=\"{0}\""; string contentH = "Content-Disposition: form-data; name=\"{0}\"" + "\n" + "\n"; string keyH = "key"; string aclH = "acl"; string aclV = "public-read"; string accessKeyH = "AWSAccessKeyId"; string accessKeyV = publicKey; string policyH = "policy"; string policyV = this.generatePolicy(); string signatureH = "signature"; string signatureV = generateSignature(policyV); string fileH = "file"; string fileContentNameH = "filename=\"{0}\""; string contentType2H = "Content-Type: application/octet-stream"; buff = boundary; addStringToStream(buff, s); buff = string.Format(contentH, keyH); addStringToStream(buff, s); buff = keyV + "\n"; addStringToStream(buff, s); buff = boundary; addStringToStream(buff, s); buff = string.Format(inLineContentH, fileH) + "; " + string.Format(fileContentNameH, Path.GetFileName(this.FilePath)) + "\n"; addStringToStream(buff, s); buff = contentType2H + "\n" + "\n"; addStringToStream(buff, s); var inStream = File.OpenRead(FilePath); int val; while ((val = inStream.ReadByte()) != -1) { s.WriteByte((byte)val); counter++; } buff = "\n"; addStringToStream(buff, s); buff = boundary; addStringToStream(buff, s); buff = string.Format(contentH, "submit"); addStringToStream(buff, s); buff = "Upload to Amazon S3" + "\n"; addStringToStream(buff, s); buff = boundary.Replace("\r", "").Replace("\n", "") + "--"; addStringToStream(buff, s); } request.GetResponse(); }
send data that goes to the stream:
---------------------------317205771417341028 Content-Disposition: form-data; name="key" webtest/image.png ---------------------------317205771417341028 Content-Disposition: form-data; name="acl" public-read ---------------------------317205771417341028 Content-Disposition: form-data; name="AWSAccessKeyId" AKIAJOQLTUC5H2NJ65NA ---------------------------317205771417341028 Content-Disposition: form-data; name="policy" Oi8vd3d3Lmdvb2dsZS[...]c3QiXSwNCiAgXQ0KfQ0K ---------------------------317205771417341028 Content-Disposition: form-data; name="Signature" 06GBK5DJ71aB[...]M8Ct8JOE= ---------------------------317205771417341028 Content-Disposition: form-data; name="file"; filename="eclipse.png" Content-Type: application/octet-stream ‰PNG IHDRn [...the rest of the image...] IEND®B‚ ---------------------------317205771417341028 Content-Disposition: form-data; name="submit" Upload to Amazon S3 ---------------------------317205771417341028--
and ... list of wires:
OUT TCP 58383 > http [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=2 SACK_PERM=1 IN TCP http > 58383 [SYN, ACK] Seq=0 Ack=1 Win=8190 Len=0 MSS=1460 WS=6 SACK_PERM=1 OUT TCP 58383 > http [ACK] Seq=1 Ack=1 Win=65700 Len=0 OUT TCP [TCP segment of a reassembled PDU] IN HTTP HTTP/1.1 100 Continue OUT TCP [TCP segment of a reassembled PDU] OUT TCP [TCP segment of a reassembled PDU] OUT TCP [TCP segment of a reassembled PDU] OUT TCP [TCP segment of a reassembled PDU] IN TCP http > 58383 [ACK] Seq=26 Ack=2031 Win=1331456 Len=0 OUT TCP [TCP segment of a reassembled PDU] OUT TCP [TCP segment of a reassembled PDU] IN TCP http > 58383 [ACK] Seq=26 Ack=3969 Win=1337344 Len=0 OUT TCP [TCP segment of a reassembled PDU] OUT TCP [TCP segment of a reassembled PDU] IN TCP http > 58383 [ACK] Seq=26 Ack=6889 Win=1343232 Len=0 OUT TCP [TCP segment of a reassembled PDU] OUT TCP [TCP segment of a reassembled PDU] IN TCP http > 58383 [ACK] Seq=26 Ack=8441 Win=1346048 Len=0 OUT HTTP POST / HTTP/1.1
the only difference between the above report and the one obtained when downloading based on web applications is that the web application has fewer [TCP segments of reassembled PDUs] and has some [TCP Dup ACK 156 # 1] http> 58364 [ACK] Sq = 1 Ack = 7017 Win = 11668 Len = 0 entries (if it can be useful, I can send complete traces).