带有文件和参数的WebRequest POST请求

13

我正在尝试使用.NET/C#上传文件并向我的网站发送一些参数。阅读了一些教程,其中有些只涉及参数或文件,我尝试将它们结合起来,但不成功。这是我的尝试:

WebRequest req = WebRequest.Create(baseURL + "upload");
req.Credentials = new NetworkCredential(username, password);
String boundary = "B0unD-Ary";
req.ContentType = "multipart/form-data; boundary=" + boundary;
req.Method = "POST";
((HttpWebRequest)req).UserAgent = "UploadTester v0.1";

string postData = "--" + boundary + "\nContent-Disposition: form-data\n";
postData += "myId=123&someFk=456";
postData += "\n--" + boundary + "\nContent-Disposition: form-data; name=\"file\" filename=\"upload.pdf\" Content-Type: application/pdf\n\n";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);

byte[] filedata = null;
using (BinaryReader reader = new BinaryReader(File.OpenRead("myfile.pdf")))
    filedata = reader.ReadBytes((int)reader.BaseStream.Length);

req.ContentLength = byteArray.Length + filedata.Length;
req.GetRequestStream().Write(byteArray, 0, byteArray.Length);
req.GetRequestStream().Write(filedata, 0, filedata.Length);

WebResponse response = req.GetResponse();
Stream data = response.GetResponseStream();
StreamReader sReader = new StreamReader(data);
String sResponse = sReader.ReadToEnd();
response.Close();

当我执行它时,出现500异常,显示“Header section has more than 10240 bytes (maybe it is not properly terminated)”,Wireshark告诉我发送的请求是一个畸形的数据包,其中MIME多部分是畸形的。
这里可能有几个问题,请让我知道你能发现的所有问题。
更新:为了将MIME与C#/.NET分开,我在这里创建了一个线程:https://stackoverflow.com/questions/1880002/error-in-mime-packet-for-http-post 更新2:因此,后端确实存在内容长度的问题,表示可读取的字节数小于声明的内容长度。但是!如果我相应地减少req.ContentLength中的内容长度,则没有足够大的缓冲区来发送数据。有什么建议吗?
更新3:实际上,看起来头部的大小与其所包含的数据量相比过大。

如果您正在使用 .NET >= 4.0,请跳到我的答案以获得更简单的方法。 - Joshcodes
3个回答

11
问题在于你缺少一个 '\n'。下面这行代码:
string postData = "--" + boundary + "\nContent-Disposition: form-data\n";

应该是:

string postData = "--" + boundary + "\nContent-Disposition: form-data\n\n";

并且这一行:

postData += "\n--" + boundary + "\nContent-Disposition: form-data; name=\"file\" filename=\"upload.pdf\" Content-Type: application/pdf\n\n"

'Content-Type'之前缺少换行符'\n'。正确应为:

postData += "\n--" + boundary + "\nContent-Disposition: form-data; name=\"file\" filename=\"upload.pdf\"\nContent-Type: application/pdf\n\n"

3
感谢您指出这些问题。经过长达15小时的调试,我找到了主要的问题:.NET引擎将所有我的换行符转换为CR+Newline,从而写入比我在content-length中指定的字节数更多的字节。因此,对于将来遇到类似问题的任何人:请用CR + newline替换所有换行符(\ n to \r\n)。 - niklassaers
当我使用这个时,会出现“Multipart: final boundary expected”错误。有任何解决方法吗? - Access Denied

1

我猜问题出在声明长度和实际长度不匹配。

你哪里算错了我不确定,但如果我没记错的话,长度应该包括响应中的每个字节(包括头部),除了第一行。

为了确保,我会建立一个简单的HTML页面来生成你想要的POST请求,并使用Fiddler(或Firebug)检查POST请求。


啊,谢谢,这是个好提示。可惜我必须手动完成这个任务。我相信成千上万的人已经写过类似的代码,如果有一个标准库就会更容易了。 - niklassaers
嗯,但我的内容长度正确设置为129597字节,远非10240。:-I - niklassaers
不要将数据写入请求流,而是写入文件流,并查看有多少字节,确保内容长度匹配。或者,将数据写入MemoryStream中,然后可以使用memoryStream.ToArray()方法获取内容长度。 - feroze
每个部分都必须有自己的内容长度标头吗?如果没有,就无法确定二进制部分的结束位置。 - mfeingold

0
尝试进行更改
string postData = "--" + boundary + "\nContent-Disposition: form-data\n";

string postData = "\n\n--" + boundary + "\nContent-Disposition: form-data\n\n";

最好将所有的\n替换为\r\n。至少对我来说,前两个换行符解决了“标头部分超过10240字节(可能未正确终止)”问题。

而且这段代码看起来不正确:

postData += "myId=123&someFk=456";

我现在正在使用并且运行良好的iPhone应用程序的Objective-C代码如下:

NSMutableData *reqData = [NSMutableData data];
NSURL *encUrl;
encUrl = [NSURL URLWithString:[NSString url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:encUrl];
request.HTTPMethod = @"POST";
request.timeoutInterval = kServerConnectionTimeout;
    NSString *stringBoundary = [NSString stringWithString:@"Multipart-Boundary"]; // TODO - randomize this
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=\"%@\"",stringBoundary];
    [request addValue:contentType forHTTPHeaderField: @"Content-Type"];

[reqData appendData:[[NSString stringWithFormat:@"\r\n\r\n--%@\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
for (id key in [self.parameters allKeys])
{
    if (![key isEqualToString:@"image"])
    {
        NSString *val = [self.parameters objectForKey:key];
        if (val != nil)
        {
            [reqData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n",key] dataUsingEncoding:NSUTF8StringEncoding]];
            [reqData appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
            [reqData appendData:[[NSString stringWithFormat:@"Content-Transfer-Encoding: 8bit\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
            [reqData appendData:[[NSString stringWithFormat:@"%@",val] dataUsingEncoding:NSUTF8StringEncoding]];
            [reqData appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
        }
    }
}

// send binary part last
if ([self.parameters objectForKey:@"image"])
{
    NSString *key  = @"image";
    NSData *imageData = [self.parameters objectForKey:key];
    if (imageData != nil)
    {
        [reqData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"image\";filename=\"avatar.png\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
        [reqData appendData:[[NSString stringWithFormat:@"Content-Type: image/png\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];

        [reqData appendData:[[NSString stringWithFormat:@"Content-Transfer-Encoding: binary\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
        [reqData appendData:imageData];
        [reqData appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
    }
}
NSString * dataLength = [NSString stringWithFormat:@"%d", [reqData length]];
[request addValue:dataLength forHTTPHeaderField:@"Content-Length"];

request.HTTPBody = reqData;
NSLog(@"postBody=%@", [[NSString alloc] initWithData:reqData encoding:NSASCIIStringEncoding]);

希望这能帮助到某些人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接