将图片上传到服务器后,图片损坏了。

3
我在我的应用程序中有一个图片上传方法,需要向服务器发送一张图片和一个字符串。
问题是服务器接收到内容(图片和字符串),但当它将图片保存到磁盘上时,图片会损坏,无法打开。
以下是脚本的相关部分。
HttpPost httpPost = new HttpPost(url);

Bitmap bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();

bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);

byte[] byteArray = stream.toByteArray();
String byteStr = new String(byteArray);

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("--"+boundary+"\r\n");
stringBuilder.append("Content-Disposition: form-data; name=\"content\"\r\n\r\n");
stringBuilder.append(message+"\r\n");

stringBuilder.append("--"+boundary+"\r\n");
stringBuilder.append("Content-Disposition: form-data; name=\"image\"; filename=\"image.jpg\"\r\n");

stringBuilder.append("Content-Type: image/jpeg\r\n\r\n");

stringBuilder.append(byteStr);
stringBuilder.append("\r\n");

stringBuilder.append("--"+boundary+"--\r\n");
StringEntity entity = new StringEntity(stringBuilder.toString());
httpPost.setEntity(entity);

我不能更换服务器,因为其他客户端在使用它并且对他们有效。我只需要了解为什么图像被损坏。


我只是猜测,但你有检查编码吗?在从图像数据创建字符串时,你没有指定字符集:new String(byteArray, ??) - Peter
你是什么意思?我该怎么做? - Nicos Karalis
2个回答

3
当您执行new String(byteArray)时,它会将二进制转换为默认字符集(通常为UTF-8)。大多数字符集不适合用于二进制数据的编码。换句话说,如果您将某些二进制字符串编码为UTF-8,然后解码回二进制,您将无法获得相同的二进制字符串。
由于您正在使用多部分编码,因此需要直接写入实体的流。 Apache HTTP Client提供了用于执行此操作的帮助程序。请参见本指南此Android指南以进行多部分上传。
如果您只需使用字符串,则可以使用以下方式安全地将字节数组转换为字符串:
String byteStr = android.util.Base64.encode(byteArray, android.util.Base64.DEFAULT);

但需要注意的是,您的服务器需要将字符串进行Base64解码,并将其保存为图像的字节数组。此外,传输大小会更大,因为Base64编码不如原始二进制编码空间效率高。


那对我们不起作用。我们不想使用那个库。如果可以的话,我更喜欢只使用字符串来发送信息。 - Nicos Karalis
@NicosKaralis 无论你喜欢什么,都无关紧要。你没有选择。你必须直接写入字节,而不是通过“字符串”。 - user207421
@NicosKaralis 如果你对服务器代码有控制权,你可以选择使用Base64编码将图像进行编码(适用于编码二进制数据),然后在服务器端解码并保存图像。这可能需要额外的工作量。 - Samuel
@NicosKaralis 如果您将代码发布到您的其他客户端之一,我们就可以告诉您为什么那个客户端有效,而这个 Android 客户端无效。很可能是其他客户端将原始二进制文件附加在末尾,而不是先转换为文本。 - Samuel
1
Swift版本使用UIImageJPEGRepresentation获取字节并将其全部放入字符串中发送到服务器。浏览器版本也将图像作为字符串发送。 - Nicos Karalis
显示剩余3条评论

0

你上面的解决方案不起作用,因为你使用了new String(byteArray)。该构造函数使用默认编码对字节数组进行编码 - 请参见什么是默认编码 - 很可能,你的数据中有无法编码为字符的字节序列。

更准确地说,字符集定义了字符如何表示为字节。大多数字符集具有超过256个字符。这就是为什么需要多个字节来表示一个字符。UTF-8和UTF-16使用最多四个字节。因此,你有一个数字空间和字符空间之间的映射,并且这个映射不是一对一的。因此,很可能存在一个数字在数字空间中没有映射到任何字符。

@Samuel提出的解决方案是万无一失的,因为Base64使用A-Z、a-z、0-9、+、/和=来表示一个字节。我更喜欢这个解决方案!

如果你不想或不能使用Base64,那么你可以尝试将每个字节按原样放入StringBuilder中,希望服务器在你获取它之前不会进行任何编码。

for (byte b : byteArray) {
  stringBuilder.append((char)b);
}

一般情况下,我不建议使用那种解决方案,但它可能会帮助你完成你的任务。


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