字节数组转换为UTF8字符串

5
我需要将一个字节数组转换成UTF8字符串,并保留数组中的字符。
我正在使用multipart post上传图像。图像被作为UTF8字符串一起发送。我已经比较了我的应用程序和Web浏览器的标头,数据是相同的,除了一个问题。
当浏览器发送内容时,内容包含许多[]字符,而我的应用程序会用?替换[]。这意味着它没有按照预期保留字符。其他所有内容都是一样的。
下面是我的代码:
Byte[] fileOpen = File.ReadAllBytes("C:/pic.jpeg");
postData.AppendLine(System.Text.Encoding.UTF8.GetString(fileOpen));

任何建议吗?

4
JPEG文件中不包含UTF8编码的文本。你想做什么? - Mark Byers
你不能将 JPG 文件作为 UTF8 文本发送。你必须将其作为 JPG 文件发送,即 image/jpeg - Tim Robinson
2
如果我没记错的话,传递二进制数据和字符串的最佳方式是先将其转换为base64。 - Alex Bagnolini
@Tim 我正在通过多部分POST请求发送日期。正如我在下面所说的,我的应用程序和浏览器之间的数据是相同的,除了浏览器头在内容中显示 [],而我的应用程序显示 ? 标志。[]之间的内容在两个请求中完全相同。 - James Jeffery
尝试附加使用诸如Wireshark之类的应用程序生成的通信日志,以显示从Web浏览器的工作上传。然后,我们应该能够弄清楚如何在C#和.NET中复制相同的行为。 - Lukas Pokorny
3个回答

7

图像以UTF8字符串形式发送。

为什么?UTF-8是一种文本编码。原始二进制数据不应该被编码,而应该直接作为字节发送。

如果您的传输协议不允许字节传输,则通常的方法是将字节编码为Base64。


这很奇怪。我无法控制我要发送到的服务器。但是在检查头部内容/正文数据时,我的应用程序和浏览器请求在 Fiddler 中看起来相似,除了我的应用程序将 [] 替换为 ?,但 [] 中间的数字和字符完全相同。如果你明白我的意思。 - James Jeffery
@James:那些不是[]字符,它们是无法显示的Unicode字符(这是将二进制文件读取为文本文件的副作用)。很可能,你应该将你的POST请求的内容编码设置为8位二进制或类似的方式。 - John Gietzen
@John,但是浏览器请求中的头部没有设置content-encoding头部,实际上,它设置了gzip和deflate。 - James Jeffery

2
不要试图使用任何接近文本API的方式发送数据。你没有说明postData是什么,但是尝试查找其API的某些部分来处理二进制数据流而不是文本数据。寻找类似于AppendBytes或GetStream的方法,以获取可以将数据写入其中的流。
假装任意二进制数据是文本是一个坏主意 - 你会丢失数据。
编辑:一种倾向于不丢失数据(但仍然是一个坏主意)的方法是将二进制数据视为ISO-8859-1编码的文档。我记得有一些关于ISO-8859-1在位置128-159包含什么的争议,但大多数编码至少认为Unicode 128-159也是如此。
你对二进制数据的“UTF-8解码”可能看起来像正确的数据,因为对于值0-127,它们是相同的 - 只有在以上情况下才会出现问题。然而,你仍然应该避免将这些二进制数据视为文本。它不是文本,将其视为文本只是制造灾难的办法。
如果你能发布浏览器发送的标头(包括与图像对应的多部分的标头),我们可以帮助你更进一步 - 但最重要的是,你应该找到一种方法将原始二进制数据传递给你正在使用的任何API,而不是通过文本。

Jon Skeen,我真希望有更好的方法来解决它,但问题是我正在发送到一个我无法控制的服务器。我知道这个服务器百分之百使用UTF8来发送图片并在他们那边解码。我希望我能向你展示头文件中的数据,你就会明白我的意思。 - James Jeffery
@James:恐怕我不相信你——我并不是说你在撒谎,只是你可能误解了数据。大多数图像根本就不是有效的UTF-8编码。如果你真的想向我们展示一些头部数据,为什么不把它放在问题中呢?它可能会发送一个作为UTF-8编码的base64字符串,但这是另一回事——如果你查看数据,应该很明显。 - Jon Skeet
只是作为进一步的说明,如果你说的是ISO-Latin-1(又称ISO-8859-1),那会更可信一些 - 请参见我的编辑。 - Jon Skeet
Jon是正确的。大多数图像如果被视为UTF-8字符串来处理,就会被破坏。尝试使用任何包含0x88的字节数组,调用UTF8.GetString后接着调用UTF8.GetBytes,看看你得到什么 - 0x88将不再存在。 - Lukas Pokorny
约翰和其他不相信我的人,请看下面的答案。我已经解决了它,而且确实是UTF8。 - James Jeffery
@James:除了你现在已经展示的,它并不是这样的。为了让它工作,你最终还是按照我的建议,直接将其写入请求流中,而不是先将其转换为字符串... - Jon Skeet

1
对于John和其他不相信我的人,我已经解决了。将其转换为字符串会导致问题,但是直接将其写入请求流中可以解决问题。
public string solveCaptcha(String username, String password)
    {
        String boundry = "---------------------------" + DateTime.Now.Ticks.ToString("x");

        StringBuilder postData = new StringBuilder();
        postData.AppendLine("--" + boundry);
        postData.AppendLine("Content-Disposition: form-data; name=\"function\"");
        postData.AppendLine("");
        postData.AppendLine("picture2");
        postData.AppendLine("--" + boundry);
        postData.AppendLine("Content-Disposition: form-data; name=\"username\"");
        postData.AppendLine("");
        postData.AppendLine(username);
        postData.AppendLine("--" + boundry);
        postData.AppendLine("Content-Disposition: form-data; name=\"password\"");
        postData.AppendLine("");
        postData.AppendLine(password);
        postData.AppendLine("--" + boundry);
        postData.AppendLine("Content-Disposition: form-data; name=\"pict\"; filename=\"pic.jpeg\"");
        postData.AppendLine("Content-Type: image/pjpeg");
        postData.AppendLine("");

        StringBuilder postData2 = new StringBuilder();
        postData2.AppendLine("\n--" + boundry);
        postData2.AppendLine("Content-Disposition: form-data; name=\"pict_to\"");
        postData2.AppendLine("");
        postData2.AppendLine("0");
        postData2.AppendLine("--" + boundry);
        postData2.AppendLine("Content-Disposition: form-data; name=\"pict_type\"");
        postData2.AppendLine("");
        postData2.AppendLine("0");
        postData2.AppendLine("--" + boundry + "--");

        Byte[] fileOpen = File.ReadAllBytes("C:/pic.jpeg");
        byte[] buffer = Encoding.ASCII.GetBytes(postData.ToString());
        byte[] buffer2 = Encoding.ASCII.GetBytes(postData2.ToString());

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://poster.decaptcher.com/");

        request.ContentType = "multipart/form-data; boundary=" + boundry;
        request.ContentLength = buffer.Length + buffer2.Length + fileOpen.Length;
        request.Method = "POST";

        String source = "";

        using (Stream PostData = request.GetRequestStream())
        {
            PostData.Write(buffer, 0, buffer.Length);
            PostData.Write(fileOpen, 0, fileOpen.Length);
            PostData.Write(buffer2, 0, buffer2.Length);

            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                Byte[] rBuf = new Byte[8192];
                Stream resStream = response.GetResponseStream();
                string tmpString = null;
                int count = 0;
                do
                {
                    count = resStream.Read(rBuf, 0, rBuf.Length);
                    if (count != 0)
                    {
                        tmpString = Encoding.ASCII.GetString(rBuf, 0, count);
                        source += tmpString;
                    }
                } while (count > 0);

            }
        }
        MessageBox.Show(source);
        // Do something with the source
        return source;
    }

如果您有deCaptcher帐户,请自行测试。如有必要,我会发布一个视频来证明它的工作原理。

Encoding.ASCII 将原样返回您的字节。这在某种程度上证明了我们的观点:您的数据并没有使用 UTF-8 编码 - 实际上,在您的代码中根本没有使用 UTF-8 编码。此外,您现在直接将实际图像数据作为字节读取,而不进行任何转换(这是好的)。那么所谓的 UTF-8 编码图片在哪里呢? - Konrad Rudolph
抱歉,我刚刚发布时才注意到需要编辑。是的,你说得对,UTF8 弄乱了它。 - James Jeffery
顺便说一句,没有人称呼你愚蠢 - 我们只是指出了一个事实,即你似乎不知道数据的正确编码方式,因此你的方法是错误的。 - Konrad Rudolph
将其转换为字符串会导致问题,但直接将其写入请求流中可行。 - 你的意思就是我在答案中所说的吗?“...以检索可以将数据写入其中的流。” 在您的工作代码中,您正如我所说的那样,直接从字节数组将数据写入请求。 - Jon Skeet

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