2

我有一个自己的ContentProvider实现,它支持通过重载ContentProvider.openInputStream和openOutputStream方法来保存/加载图像到每个记录。本地一切工作正常。但现在我正在从URL下载图像,然后将它们保存到ContentProvider中。在ContentProvider中下载和存储图像

最佳的解决方案,但不起作用:为了避免在内存中创建巨大的位图,我想直接将传入的HTTPS流写入文件(选项1)。但是当我加载位图时,BitmapFactory会引发错误。

作品,但没有最佳的解决方案:如果我从传入HTTPS数据流(选项2)到内存中加载位图,然后保存(压缩),它的ContentProvider的 - 然后加载位以后工作正常。

所以我想知道我在做什么错了?

这里是一些要测试的网址:

https://lh5.ggpht.com/goggZXKLiJst1uSWPmgzk9j2WqdNiPAQZyb59tddL1WIHQgb-cPV7uqGuqECdu7ChiW8vve_2UC-Ta16YfbLlA=s192 https://lh4.ggpht.com/EizCbwoyAndISCf1b2tjPkOSMEl-jJZoPJ386RtQ7Q4kJ-1tUDEhqweXrPP-jX7pbCAoCUYN7iw1beyiI9JTFAo=s160

示例代码(downloadDirect导致错误的位图,downloadIndirect作品):

private void downloadDirect(String url, int key, Context context) 
     throws MalformedURLException, IOException { 
    InputStream is = download(url); 

    OutputStream os = openOutputStream(context, key); 
    final byte[] buffer = new byte[BUFFER_SIZE]; 

    while (is.read(buffer) >= 0) { 
     os.write(buffer); 
    } 

    os.close(); 
    is.close(); 
} 

private void downloadIndirect(String url, int key, Context context) 
     throws MalformedURLException, IOException { 
    InputStream is = download(url); 

    Bitmap bitmap = BitmapFactory.decodeStream(is); 
    saveBitmap(context, key, bitmap); 
} 

private InputStream download(String url) throws MalformedURLException, 
     IOException { 

    URL newUrl = new URL(url); 
    HttpURLConnection con = (HttpURLConnection) newUrl.openConnection(); 
    return con.getInputStream(); 
} 

这些都是ContentProvider的方法:

public static InputStream openInputStream(Context context, Uri contentUri, 
     int key) throws FileNotFoundException { 

    Uri uri = ContentUris.withAppendedId(contentUri, key); 
    return context.getContentResolver().openInputStream(uri); 
} 

public static OutputStream openOutputStream(Context context, 
     Uri contentUri, int key) throws FileNotFoundException { 

    Uri uri = ContentUris.withAppendedId(contentUri, key); 
    return context.getContentResolver().openOutputStream(uri); 
} 

protected static void saveBitmap(Context context, Uri contentUri, 
     String basePath, int key, Bitmap value, boolean updateDatabase) { 

    Uri uri = ContentUris.withAppendedId(contentUri, key); 

    try { 
     if (value == null) { 
      deleteFile(uri, basePath, context, true); 
      return; 
     } 

     OutputStream outStream; 
     try { 
      outStream = openOutputStream(context, contentUri, key); 
      ImageUtils.saveToStream(value, outStream, 
        Bitmap.CompressFormat.PNG); 
      outStream.close(); 

      Log.d(TAG, 
        "Image (" + value.getWidth() + "x" + value.getHeight() 
          + "pixels) saved to " + uri.toString()); 

     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
      Log.e(TAG, "Could not save image to " + uri.toString()); 
     } catch (IOException e) { 
      e.printStackTrace(); 
      Log.e(TAG, "Could not save image to " + uri.toString()); 
     } 
    } finally { 
     if (updateDatabase) { 
      ContentValues values = new ContentValues(); 
      // modified column will be added automatically 
      context.getContentResolver().update(uri, values, null, null); 
     } 
    } 
} 

所述的ContentProvider的中openFile方法重写这样的:

public ParcelFileDescriptor openFile(Uri uri, String mode) 
     throws FileNotFoundException { 

    ContextWrapper cw = new ContextWrapper(getContext()); 

    // path to /data/data/yourapp/app_data/dir 
    File directory = cw.getDir(basePath, Context.MODE_MULTI_PROCESS); 
    directory.mkdirs(); 

    long id = ContentUris.parseId(uri); 
    File path = new File(directory, String.valueOf(id)); 

    int imode = 0; 
    if (mode.contains("w")) { 
     imode |= ParcelFileDescriptor.MODE_WRITE_ONLY; 
     if (!path.exists()) { 
      try { 
       path.createNewFile(); 
      } catch (IOException e) { 
       Log.e(tag, "Could not create file: " + path.toString()); 
       e.printStackTrace(); 
      } 
     } 
    } 
    if (mode.contains("r")) 
     imode |= ParcelFileDescriptor.MODE_READ_ONLY; 
    if (mode.contains("+")) 
     imode |= ParcelFileDescriptor.MODE_APPEND; 

    return ParcelFileDescriptor.open(path, imode); 
} 
+0

'保存/加载图像到每个记录???对每个记录?对不起,我已经不在线了。 – greenapps

+0

您没有显示'openOutputStream(context,key)的代码;'并没有使用'downloadDirect()'和原始大小来告诉保存文件的大小。 – greenapps

+0

对不起,我添加了方法openFile(...),您必须重写,以便ContentResolver.openOutputStream(...)在上面工作。我发现问题了。我只是以错误的方式阅读输入流。这导致了错误的文件。 – Matthias

回答

3

我被以错误的方式读取所述HTTP输入流。但是也有一些可以正常使用两个正确的方式:

一个)直接写入到输出流

private void downloadDirect(String url, int key, Context context) 
     throws MalformedURLException, IOException { 

    InputStream is = download(url); 
    OutputStream os = openOutputStream(context, key); 


    final byte[] buffer = new byte[BUFFER_SIZE]; 

    int bytesRead = 0; 
    while ((bytesRead = is.read(buffer, 0, buffer.length)) >= 0) { 
     os.write(buffer, 0, bytesRead); 
    } 

    os.close(); 
    is.close(); 
} 

b)中写入一个缓冲流,然后到输出流

private void downloadDirect(String url, int key, Context context) 
     throws MalformedURLException, IOException { 

    InputStream is = download(url); 
    OutputStream os = openOutputStream(context, key); 

    BufferedInputStream bis = new BufferedInputStream(is); 

    ByteArrayBuffer baf = new ByteArrayBuffer(50); 
    int current = 0; 
    while ((current = bis.read()) != -1) { 
     baf.append((byte) current); 
    } 

    os.write(baf.toByteArray()); 

    os.close(); 
    is.close(); 
} 

的这种解决方案的优点是,即使是大型图像文件也可以下载并直接存储到文件中。稍后,您可以使用采样将这些(可能较大的)图像加载到位图中。

我在互联网上发现的大多数文章都使用HTTP输入流来使用BitmapFactory解码位图。这是一种非常糟糕的方法,因为如果图像太大,您可能会发生OutOfMemoryException。

而且速度也很慢,因为首先将输入流解码为位图,然后再将位图编码为输出流。糟糕的表现。