3

我有一个控制台应用程序,它将图像编码为 base64String,然后将其作为 http POST 请求的一部分发送到服务器。

问题是,当我打开它检查服务器端时,数据不再存在。

这是我用来发送字符串的代码-

HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(ip);
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Method = "POST";
string formPostString = "image=" + HttpUtility.UrlEncode(imageBase64String);
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(formPostString);
webRequest.ContentLength = buffer.Length;
using (Stream webStream = webRequest.GetRequestStream())
{
    webStream.Write(buffer, 0, buffer.Length);
}

我在服务器端处理它的代码-

private void OnRequest(object source, RequestEventArgs args)
{
    IHttpClientContext context = (IHttpClientContext)source;
    IHttpRequest request = args.Request;

    ImageConverter imgConvert = new ImageConverter();
    string imageBase64String = request.Form["image"].Value;
    Image newImg = imgConvert.Base64StringToImage(imageBase64String);
    newImg.Save(@"..\Images\test.png", System.Drawing.Imaging.ImageFormat.Png);
}

当它到达创建新 Image 对象的点时,imageBase64String 为空。我似乎无法确定为什么会这样。

4

1 回答 1

3

您显示的客户端代码从不发送请求。您必须致电webRequest.GetResponse()要求发送请求。但我会使用WebClient简化此客户端代码:

using (var client = new WebClient())
{
    var data = new NameValueCollection
    {
        { "image", imageBase64String }
    };
    var result = client.UploadValues(ip, data);
    Console.WriteLine(Encoding.Default.GetString(result));
}

就您的服务器端代码而言,您似乎正在使用一些您在问题中没有提到的第三方框架。例如,以下 ASP.NET 处理程序与前面显示的客户端完美~/UploadImage.ashx配合 ( ):

public class UploadImage : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var imageBase64String = context.Request.Form["image"];
        var imageBuffer = Convert.FromBase64String(imageBase64String);
        using (var stream = new MemoryStream(imageBuffer))
        using (var image = Image.FromStream(stream))
        {
            image.Save(@"c:\work\test.png");
        }

        context.Response.ContentType = "text/plain";
        context.Response.Write("upload successful");
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

但是如果我们要将文件上传到服务器,为什么要重新发明轮子呢?为什么使用 base64 时使用application/x-www-form-urlencoded重新发明的 Base64 编码会极大地增加请求大小multipart/form-data

因此,让我们改进我们的客户端,以利用这种专门为目的编码而设计的优势。让我们从为 WebClient 编写一个扩展方法开始,它允许我们直接上传一个或多个文件:

public class UploadFile
{
    public UploadFile()
    {
        ContentType = "application/octet-stream";
    }
    public string Name { get; set; }
    public string Filename { get; set; }
    public string ContentType { get; set; }
    public Stream Stream { get; set; }
}

public static class WebClientExtensions
{
    public static byte[] UploadFiles(this WebClient client, string address, IEnumerable<UploadFile> files, NameValueCollection values)
    {
        var request = WebRequest.Create(address);
        request.Method = "POST";
        var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo);
        request.ContentType = "multipart/form-data; boundary=" + boundary;
        boundary = "--" + boundary;

        using (var requestStream = request.GetRequestStream())
        {
            // Write the values
            foreach (string name in values.Keys)
            {
                var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
                requestStream.Write(buffer, 0, buffer.Length);
                buffer = Encoding.ASCII.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"{1}{1}", name, Environment.NewLine));
                requestStream.Write(buffer, 0, buffer.Length);
                buffer = Encoding.UTF8.GetBytes(values[name] + Environment.NewLine);
                requestStream.Write(buffer, 0, buffer.Length);
            }

            // Write the files
            foreach (var file in files)
            {
                var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
                requestStream.Write(buffer, 0, buffer.Length);
                buffer = Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"{2}", file.Name, file.Filename, Environment.NewLine));
                requestStream.Write(buffer, 0, buffer.Length);
                buffer = Encoding.ASCII.GetBytes(string.Format("Content-Type: {0}{1}{1}", file.ContentType, Environment.NewLine));
                requestStream.Write(buffer, 0, buffer.Length);
                file.Stream.CopyTo(requestStream);
                buffer = Encoding.ASCII.GetBytes(Environment.NewLine);
                requestStream.Write(buffer, 0, buffer.Length);
            }

            var boundaryBuffer = Encoding.ASCII.GetBytes(boundary + "--");
            requestStream.Write(boundaryBuffer, 0, boundaryBuffer.Length);
        }

        using (var response = request.GetResponse())
        using (var responseStream = response.GetResponseStream())
        using (var stream = new MemoryStream())
        {
            responseStream.CopyTo(stream);
            return stream.ToArray();
        }
    }
}

然后客户端将如下所示:

byte[] image = ... go and fetch the image that you want to upload

using (var stream = new MemoryStream(image))
using (var client = new WebClient())
{
    var files = new[]
    {
        new UploadFile
        {
            Name = "image",
            Filename = "test.jpg",
            ContentType = "image/jpg",
            Stream = stream
        }
    };
    var result = client.UploadFiles("http://localhost:6830/uploadimage.ashx", files, new NameValueCollection());
    Console.WriteLine(Encoding.Default.GetString(result));
}

以及我们之前看到的我们处理的服务器:

public class UploadImage : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var uploadedImage = context.Request.Files["image"];
        using (var image = Image.FromStream(uploadedImage.InputStream))
        {
            image.Save(@"c:\work\test.png");
        }

        context.Response.ContentType = "text/plain";
        context.Response.Write("upload successful");
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

猜猜,改进此代码的下一步是什么?答:一个异步客户端请求当然会利用 I/O 完成端口,而不是在等待文件上传到服务器时阻塞调用线程。

于 2012-07-13T20:24:48.687 回答