【问题标题】:OutOfMemoryError when sending image from android to php server将图像从 android 发送到 php 服务器时出现 OutOfMemoryError
【发布时间】:2013-09-11 15:46:28
【问题描述】:

我正在尝试将图像从我的 sdcard 发送到 php 服务器... 我创建了 2 个类:第一个是 MainActivity.java,第二个是 Base64.java,它以 Base64 格式编码字节。

我的 MainActivity.java 是:

public class MainActivity extends Activity {
    InputStream inputStream;
    @Override
public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.activity_main);

        Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/DCIM/Camera/1378889572299.jpg");
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream); //compress to which format you want.
        byte [] byte_arr = stream.toByteArray();
        String image_str = Base64.encodeBytes(byte_arr);
         ArrayList<NameValuePair> nameValuePairs = new  ArrayList<NameValuePair>();

        nameValuePairs.add(new BasicNameValuePair("image",image_str));
Log.e("ok", "1");
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://myserver.com/upload_image.php");
Log.e("ok", "3");
try {
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
Log.e("ok", "4");
HttpResponse response = httpclient.execute(httppost);
Log.e("ok", "5");

    String the_string_response = convertResponseToString(response);
} catch (IllegalStateException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

    }

    public String convertResponseToString(HttpResponse response) throws IllegalStateException, IOException{

        String res = "";
         StringBuffer buffer = new StringBuffer();
         inputStream = response.getEntity().getContent();
         final int contentLength = (int) response.getEntity().getContentLength(); //getting content length…..
          runOnUiThread(new Runnable() {

        @Override
        public void run() {
            Toast.makeText(MainActivity.this, "contentLength : " + contentLength, Toast.LENGTH_LONG).show();                    
        }
    });

         if (contentLength < 0){
         }
         else{
                byte[] data = new byte[512];
                int len = 0;
                try
                {
                    while (-1 != (len = inputStream.read(data)) )
                    {
                        buffer.append(new String(data, 0, len)); //converting to string and appending  to stringbuffer…..
                    }
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
                try
                {
                    inputStream.close(); // closing the stream…..
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
                res = buffer.toString();     // converting stringbuffer to string…..
                final String res2 = res;
                runOnUiThread(new Runnable() {

                @Override
                public void run() {
                   Toast.makeText(MainActivity.this, "Result : " + res2, Toast.LENGTH_LONG).show();
                }
            });
                //System.out.println("Response => " +  EntityUtils.toString(response.getEntity()));
         }
         return res;
    }
}

我有这个错误:

........OutOfMemoryError
….at java.lang.AbstractStringBuilder.(AbstractStringBuilder.java:81)
….at java.lang.StringBuilder.(StringBuilder.java:68)
….at java.net.URLEncoder.encode(URLEncoder.java:98)
….at org.apache.http.client.utils.URLEncodedUtils.encode(URLEncodedUtils.java:184)
….at org.apache.http.client.utils.URLEncodedUtils.format(URLEncodedUtils.java:163)
….at org.apache.http.client.entity.UrlEncodedFormEntity.(UrlEncodedFormEntity.java:71)
….at com.example.imageuploadonserver.MainActivity.onCreate(MainActivity.java:43)

第 43 行是: httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

我对我的代码进行了一些更改...并将图像源从我的 sdcard 替换为我的 drawabel.ic_luncher:

Bitmap bitmap = BitmapFactory.decodeFile(“/sdcard/DCIM/Camera/1378889572299.jpg”);

通过这个:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);

它成功了!!!!

我搜索了很多,但要么我使用了错误的关键字,要么互联网上没有简单的解决方案。我希望这里有人可以帮助我。

提前致以最诚挚的问候和感谢,法德尔。

【问题讨论】:

  • 你的图片有多大? (不是文件大小,而是像素)
  • 我的图片尺寸:宽度:2560 高度:1920
  • 我的图片类型:image.jpeg

标签: java php android


【解决方案1】:

假设您的图像大约是 5Mo,因为它是您相机拍摄的照片。

  1. 当您将其作为位图加载时,您的堆大小会增加 5Mo。
  2. 然后您将其转换为带有 som 压缩的字节数组,因此您的堆大小可能再次增长 4Mo => 9Mo
  3. 然后你base64encode它到一个字符串,堆再次增长4Mo => 13Mo
  4. 然后,通过将它作为字符串添加到 NameValu¨Pair,我认为它会复制字符串(此处不确定)并且堆再次增长 => 17Mo
  5. 然后当您将其添加为实体时,它会对内容进行编码,并且堆再次增长 => 22Mo

当你不再需要它时,你应该尝试释放它:

  1. 在第 2 步之后使用 bitmap.recycle()。
  2. 在第 3 步之后将您的咬合数组设置为空
  3. 在第 4 步之后将字符串设置为 null(不确定)

这样做会释放大约 9 到 14 个月

但在所有情况下,通过对其进行编码(base64Encode 或 UrlEncode),您的堆大小将增长为位图大小的两倍。更好的方法应该是将文件的内容写入 HTTP 流,然后从文件中读取。

【讨论】:

  • 您的意思是在将图像发送到服务器之前以 base64 编码图像并不重要吗? @Zoubiock
  • 不,我的意思是你必须释放你不再需要的内存。之后,当你对一个对象进行编码时,它会在编码过程中存储两次,所以如果你能避免这一步(通过服务器端的改变)会更好。
  • 啊啊啊!!好啊!!!我会试试 !!如果它是正确的,我会把你的答案当真(y)......
【解决方案2】:

您要上传的图片似乎很大。您需要在创建位图时将其缩小。您的 ic_launcher png 的分辨率非常低,因此在创建位图时不会造成任何问题。

检查此链接以有效加载位图- http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

希望对您有所帮助.. :)

【讨论】:

  • mmmmmmmm .... 我觉得你是对的!!!如果它是正确的,我会把你的答案当真(y)
  • 天哪!!!非常感谢你@AshishKumar .... !!!知道我明白发生了什么!是的,它是关于图像分辨率的......有效 !!!再次非常感谢你 Ashish !!! :) :) :) :)
【解决方案3】:

为了避免一次将所有数据加载到内存中,我会使用ChunkedOutputStream 以块的形式流式传输数据。如果您对此小心,您可以上传无限大小的图像,因为它从来没有必要将整个内容都保存在内存中。

【讨论】:

    【解决方案4】:

    在处理客户端 服务器 WebServices 时,将整个请求或响应数据加载到字符串或任何内存对象中是不好的做法,应尽可能避免。在很多情况下都是可以避免的。

    为什么要避免?

    因为在许多情况下,您永远无法保证数据的大小,并且通常会导致您出现 OOM 异常。

    你应该怎么做?

    对于大多数请求数据,您可以在发送数据时构建它(XML、JSON、任何序列化),并且您不需要在内存中重新读取整个结构来序列化它。

    对于大多数答案数据,您可以从流中解析对象并将其保存在数据库中,或者丢弃流中不需要的任何部分。无需将整个响应加载为字符串。

    在您的特定情况下,您在磁盘上有一个文件是您要压缩的图像。使用标准库或任何其他图像库将其转换为 90% JPEG 到另一个文件。然后您可以将文件直接流式传输到 httpPost.entity 的输出流。

    甚至有可能某些库(甚至可能是标准库?)为您提供了直接从您的第一个图像文件中获取带有压缩图像的 InputStream 的可能性。

    在这两种情况下,将您的第一个 InputStream 封装在另一个 InputStream 中,该 InputStream 进行 Base64 转换,您就可以了。

    这样一来,对于流缓冲区,您就无需一次将超过几千字节的数据加载到内存中。

    【讨论】:

      猜你喜欢
      • 2015-07-16
      • 2015-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多