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。

而且速度也很慢,因爲首先將輸入流解碼爲位圖,然後再將位圖編碼爲輸出流。糟糕的表現。