0

我在 ASP.NET 中创建了一个 HttpModule 以允许用户上传大文件。我在网上找到了一些示例代码,我可以根据自己的需要进行调整。如果文件是多部分消息,我会抓取该文件,然后将字节分块并将它们写入磁盘。

问题是文件总是损坏。在做了一些研究之后,事实证明,由于某种原因,我收到的字节的第一部分应用了 HTTP 标头或消息正文标签。我似乎无法弄清楚如何解析这些字节,所以我只得到文件。

额外的数据/垃圾被附加到文件的顶部,例如:

-----------------------8cbb435d6837a3f
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: application/octet-stream

这种头信息当然会破坏我收到的文件,所以我需要在写入字节之前删除它。

这是我为处理上传而编写的代码:

public class FileUploadManager : IHttpModule
{
    public int BUFFER_SIZE = 1024;

    protected void app_BeginRequest(object sender, EventArgs e)
    {
        // get the context we are working under
        HttpContext context = ((HttpApplication)sender).Context;

        // make sure this is multi-part data
        if (context.Request.ContentType.IndexOf("multipart/form-data") == -1)
        {
            return;
        }

        IServiceProvider provider = (IServiceProvider)context;
        HttpWorkerRequest wr = 
        (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));

        // only process this file if it has a body and is not already preloaded
        if (wr.HasEntityBody() && !wr.IsEntireEntityBodyIsPreloaded())
        {
            // get the total length of the body
            int iRequestLength = wr.GetTotalEntityBodyLength();

            // get the initial bytes loaded
            int iReceivedBytes = wr.GetPreloadedEntityBodyLength();

            // open file stream to write bytes to
            using (System.IO.FileStream fs = 
            new System.IO.FileStream(
               @"C:\tempfiles\test.txt", 
               System.IO.FileMode.CreateNew))
            {
                // *** NOTE: This is where I think I need to filter the bytes 
                // received to get rid of the junk data but I am unsure how to 
                // do this?

                int bytesRead = BUFFER_SIZE;
                // Create an input buffer to store the incomming data 
                byte[] byteBuffer = new byte[BUFFER_SIZE];
                while ((iRequestLength - iReceivedBytes) >= bytesRead)
                {
                    // read the next chunk of the file
                    bytesRead = wr.ReadEntityBody(byteBuffer, byteBuffer.Length);
                    fs.Write(byteBuffer, 0, byteBuffer.Length);
                    iReceivedBytes += bytesRead;

                    // write bytes so far of file to disk
                    fs.Flush();
                }
            }
        }
    }
}

我将如何检测和解析此标头垃圾信息以仅隔离文件位?

4

2 回答 2

1

使用 InputStramEntity 类如下:

 InputStreamEntity reqEntity = new InputStreamEntity(new FileInputStream(filePath), -1);
 reqEntity.setContentType("binary/octet-stream");
 httppost.setEntity(reqEntity);
 HttpResponse response = httpclient.execute(httppost);

如果你像上面那样使用,它不会在服务器的 header 和 trailing 和 content-disposition、content-type 中添加令牌

-----------------------8cbb435d6837a3f 内容处置:表单数据;名称=“文件”;文件名="test.txt" 内容类型:应用程序/八位字节流

----------------------8cbb435d6837a3f

于 2013-09-22T17:53:15.090 回答
0

您遇到的是用于分隔 HTTP 请求的各个部分的边界。在请求的开头应该有一个名为 Content-type 的标头,在该标头中,有一个边界语句,如下所示:

Content-Type: multipart/mixed;boundary=gc0p4Jq0M2Yt08jU534c0p

找到此边界后,只需在边界上添加两个连字符 (--) 即可分割您的请求。换句话说,将您的内容拆分为:

"--"+Headers.Get("Content-Type").Split("boundary=")[1]

Sorta 伪代码在那里,但它应该明白这一点。这应该将多部分表单数据分成适当的部分。

有关详细信息,请参阅RFC1341

值得注意的是,最终边界显然也有两个连字符附加到边界的末尾。

编辑:好的,所以您遇到的问题是您没有将表单数据分解为必要的组件。multipart/form-data 请求的每个部分都可以单独视为单独的请求(这意味着它们可以包含标头)。您可能应该做的是将字节读入字符串:

string formData = Encoding.ASCII.GetString(byteBuffer);

根据边界拆分为多个字符串:

string boundary = "\r\n"+context.Request.ContentType.Split("boundary=")[1];
string[] parts = Regex.Split( formData, boundary );

循环遍历每个字符串,将标题与内容分开。由于您实际上想要内容的字节值,因此请跟踪数据偏移量,因为从 ASCII 转换回字节可能无法正常工作(我可能是错的,但我很偏执):

int dataOffset = 0;
for( int i=0; i < parts.Length; i++ ){
    string header = part.Substring( 0, part.IndexOf( "\r\n\r\n" ) );
    dataOffset += boundary.Length + header.Length + 4;
    string asciiBody = part.Substring( part.IndexOf( "\r\n\r\n" ) + 4 );
    byte[] body = new byte[ asciiBody.Length ];

    for( int j=dataOffset,k=0; j < asciiBody.Length; j++ ){
        body[k++] = byteBuffer[j];
    }

    // body now contains your binary data
}

注意:这是未经测试的,因此可能需要一些调整。

于 2009-06-06T04:27:27.263 回答