12

J'ai quelques gros fichiers (images et vidéo) que je dois stocker dans un fournisseur de contenu. La documentation android indique ...Comment stocker de gros blobs dans un fournisseur de contenu android?

Si vous exposiez des données d'octets qui est trop grand pour mettre dans la table elle-même - comme un grand fichier bitmap - le champ qui expose les données aux clients devraient contiennent en fait un contenu: URI chaîne. C'est le champ qui donne aux clients l'accès au fichier de données. L'enregistrement doit également avoir un autre champ, nommé "_data" qui répertorie le chemin exact du fichier sur le périphérique pour ce fichier. Ce champ ne doit pas être lu par le client, mais par le ContentResolver. Le client appellera ContentResolver.openInputStream() sur le champ de l'utilisateur contenant l'URI pour l'article. Le ContentResolver sera demande le champ « _data » pour cet enregistrement , et parce qu'il a autorisations plus élevées qu'un client, il devrait pouvoir accéder à ce fichier directement et retourner une enveloppe de lecture pour le fichier au client. - http://developer.android.com/guide/topics/providers/content-providers.html#creating

J'ai quelques difficultés à trouver un exemple. En particulier, je souhaite utiliser le bitmap dans le contexte d'un ImageView. Considérez le code suivant quasi-code (il ne fonctionne pas) ...

ImageView iv = .... 
String iconUri = cursor.getString(cursor.getColumnIndex(Table.ICON)); 
iv.setImageURI(Uri.parse(iconUri)); 

Observations/Problèmes ...

  1. Comment le stockées/uri récupéré reconstruire correctement? (c'est du texte dans la table)
  2. L'implémentation de setImageURI utilise le contenu pour résoudre openInputStream, cela devrait donc fonctionner.

    String scheme = mUri.getScheme(); 
    ... 
    } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) 
         || ContentResolver.SCHEME_FILE.equals(scheme)) { 
        try { 
        d = Drawable.createFromStream(
          mContext.getContentResolver().openInputStream(mUri), 
          null); 
    

    --frameworks/base/core/java/android/widget/ImageView.java

Je l'ai travail. J'ai consulté le MediaStore et le MediaProvider. Les fichiers contenant les données sont nommés en fonction du fournisseur de contenu (répertoire), du nom de la colonne, de l'identifiant de ligne et du type de média. Le résolveur de contenu acquiert alors le descripteur de fichier comme si ...

Uri iconUri = Uri.withAppendedPath(Table.getUri(cursor), Table.ICON); 
ib.setImageURI(iconUri); 

... et le fournisseur de contenu répond en nature ...

@Override 
public ParcelFileDescriptor openFile (Uri uri, String mode) { 
int imode = 0; 
if (mode.contains("w")) imode |= ParcelFileDescriptor.MODE_WRITE_ONLY; 
if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY; 
if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND; 
List<String> pseg = uri.getPathSegments(); 
if (pseg.size() < 3) return null; 

try { 
    File filePath = filePathFromRecord(pseg.get(2), pseg.get(1)); 
    return ParcelFileDescriptor.open(filePath, imode); 
} catch (FileNotFoundException e) { 
    e.printStackTrace(); 
} 
return null; 
} 
+0

Oops! J'ai répondu à ma propre question qui est "Comment stocker de gros blobs dans et fournisseur de contenu android" en éditant le texte original plutôt que d'afficher une réponse. En tout cas, la réponse courte est que vous utilisez openFile et vos amis de la manière décrite. – phreed

Répondre

11

La solution phreed donne dans la moitié inférieure de la question est fondamentalement correct.J'essaierai d'ajouter plus de détails ici.

Lorsque vous faites getContentResolver().openInputStream(...), le résolveur de contenu ira à votre fournisseur de contenu et appellera sa méthode openFile. Voici comment le openFile regarde dans ContentProvider.java:

public ParcelFileDescriptor openFile(Uri uri, String mode) 
    throws FileNotFoundException { 
throw new FileNotFoundException("No files supported by provider at " 
     + uri); 
} 

donc ce qui explique où l'erreur « Aucun fichier pris en charge ... » vient exactement! Vous contournez cela en remplaçant la méthode openFile dans votre sous-classe et en fournissant votre propre implémentation. C'est soigné: vous obtenez un contrôle parfait de l'endroit où vos fichiers sont placés lorsqu'un client fait openInputStream ou openOutputStream.

L'exemple de code dans la question de phreed donne un indice sur l'apparence de la mise en œuvre. Voici ma version légèrement modifiée qui crée également des répertoires et des fichiers au besoin. Je suis novice dans ce domaine, donc ce n'est peut-être pas la meilleure façon de faire les choses, mais ça donne une idée. D'une part, il devrait probablement vérifier si le stockage externe est disponible.

@Override 
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 
    File root = new File(Environment.getExternalStorageDirectory(), 
      "/Android/data/com.example.myapp/cache"); 
    root.mkdirs(); 
    File path = new File(root, uri.getEncodedPath()); 
    // So, if the uri was content://com.example.myapp/some/data.xml, 
    // we'll end up accessing /Android/data/com.example.myapp/cache/some/data.xml 

    int imode = 0; 
    if (mode.contains("w")) { 
      imode |= ParcelFileDescriptor.MODE_WRITE_ONLY; 
      if (!path.exists()) { 
       try { 
        path.createNewFile(); 
       } catch (IOException e) { 
        // TODO decide what to do about it, whom to notify... 
        e.printStackTrace(); 
       } 
      } 
    } 
    if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY; 
    if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND;   

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

Bonjour @Peteris Caune.J'ai implémenté la méthode openFile comme vous, mais je ne sais toujours pas comment écrire mon fichier (* .png) en utilisant l'objet ParcelFileDescriptor de openFileMethod(), et maintenant comment le champ '" _data "' est rempli. J'ai passé beaucoup de temps à chercher un exemple ou une explication à propos de. merci – AlexSanchez

+0

je le résous par moi-même, il semble que le champ '_data' est exclusif à l'utilisation du fournisseur, donc j'ai essayé d'aller chercher des données comme un champ normal dans le curseur. J'ai déjà remarqué que j'ai besoin d'utiliser la méthode 'mResolver.openInputStream (resourceUri);' à la place. merci btw. – AlexSanchez

0

Il y a un problème si l'écriture des fichiers. Comment le fournisseur de contenu sait-il que l'écriture est terminée?

Lors de la fermeture d'un OutputStream obtenu via un ContentResolver.openOutputStream(), je souhaite que le ContentProvider (personnalisé) correspondant effectue une action personnalisée. Actuellement, j'utilise la création d'unsur un temporary file mais il semble que ce soit la mauvaise façon de le faire. Ma première pensée a été de sous-typer le ParcelFileDescriptor produit par la méthode ContentProvider.openFile() mais cela n'a pas semblé fonctionner pour moi. Ceci étant dit, certaines erreurs ont entraîné l'utilisation de MyParcelFileDescriptor.

  • Je n'ai pas vérifié que le descripteur de fichier ne serait pas collecté prématurément.
  • Je n'ai pas essayé de remplaçant finalement() (la documentation semble suggérer que d'être l'endroit pour le faire, mais close() semblait être le bon choix pour moi.

Bas ligne, y at-il une interaction entre les objets de fichier comme vu par le ContentResolver et ceux de ContentProvider?

+0

Il y a cependant un problème d'écriture de fichiers. Comment le fournisseur de contenu sait-il que l'écriture est terminée? http://stackoverflow.com/questions/4147763/android-content-provider-custom-openfile-onclose-behavior – phreed

+0

Les [docs] (http://developer.android.com/reference/android/content/ContentProvider.html #openFile (android.net.Uri,% 20java.lang.String)) dit: * ... Le ParcelFileDescriptor retourné appartient à l'appelant, il est donc de leur responsabilité de le fermer une fois terminé. Autrement dit, l'implémentation de cette méthode devrait créer un nouveau ParcelFileDescriptor pour chaque appel. * Vous n'avez donc pas besoin de vous inquiéter de la fermeture de l'instance de 'ParcelFileDescriptor'. –

1

Android fournit la méthode d'assistance openFileHelper() qui facilite l'implémentation de la méthode openFile(). Tout ce que vous avez à faire, pour utiliser cette méthode, est de fournir l'emplacement du fichier dans une colonne nommée "_data".

@Override 
public ParcelFileDescriptor openFile(Uri uri, String mode) 
     throws FileNotFoundException { 
    if (URI_MATCHER.match(uri) != PHOTO_ID) { 
     throw new IllegalArgumentException 
      ("URI invalid. Use an id-based URI only."); 
    } 
    return openFileHelper(uri, mode); 
} 

Click here for detail

Questions connexes