android - Get filepath from google drive in Lollipop (MediaStore.MediaColumns.DATA == null) -


when user clicks "send file" button in google drive , selects app. want filepath of file , allow user upload different location.

i check these similar post kitkat phones: get real path uri, android kitkat new storage access framework

android - convert uri file path on lollipop

however solution no longer seems work in lollipop devices.

the problem seems mediastore.mediacolumns.data returns null when running query on contentresolver.

https://code.google.com/p/android/issues/detail?id=63651

you should use contentresolver.openfiledescriptor() instead of trying raw filesystem path. "_data" column not part of category_openable contract, drive not required return it.

i've read blog post commonsware suggest "try using uri directly contentresolver" don't understand. how use uri directly contentresolvers?

however, i'm still not clear on how best approach these types of uris.

the best solution i've been able find call openfiledescriptor , copy filestream new file, passing new file path upload activity.

 private static string getdrivefileabsolutepath(activity context, uri uri) {     if (uri == null) return null;     contentresolver resolver = context.getcontentresolver();     fileinputstream input = null;     fileoutputstream output = null;     string outputfilepath = new file(context.getcachedir(), filename).getabsolutepath();     try {         parcelfiledescriptor pfd = resolver.openfiledescriptor(uri, "r");         filedescriptor fd = pfd.getfiledescriptor();         input = new fileinputstream(fd);         output = new fileoutputstream(outputfilepath);         int read = 0;         byte[] bytes = new byte[4096];         while ((read = input.read(bytes)) != -1) {             output.write(bytes, 0, read);         }         return new file(outputfilepath).getabsolutepath();     } catch (ioexception ignored) {         // nothing can     } {             input.close();             output.close();     }     return ""; } 

the problem here lose filename of file. seems bit on complicated filepath drive. there better way this?

thanks.

edit: can use normal query filename. can pass getdriveabsolutepath() method. me pretty close want, problem i'm missing file extensions. searches i've done recommend using filepath extensions, can't openfiledescriptor(). help?

    string filename = "";     final string[] projection = {             mediastore.mediacolumns.display_name     };     contentresolver cr = context.getapplicationcontext().getcontentresolver();     cursor metacursor = cr.query(uri, projection, null, null, null);     if (metacursor != null) {         try {             if (metacursor.movetofirst()) {                 filename = metacursor.getstring(0);             }         } {             metacursor.close();         }     } 

however, i'm not entirely convinced "right" way this?

the problem here lose filename of file. seems bit on complicated filepath drive. there better way this?

you seem miss important point here. files in linux don't need have name. may exist in memory (e.g. android.os.memoryfile) or reside in directory without having name (such files, created o_tmpfile flag). need have file descriptor.


short summary: file descriptors better simple files , should used instead, unless closing them after of burden. can employed same things file objects, , more, if can use jni. made available special contentprovider , can accessed via openfiledescriptor method of contentresolver (which receives uri, associated target provider).

that said, saying people, used file objects, replace them descriptors sure sounds weird. read elaborate explanation below, if feel trying out. if don't, skip bottom of answer "simple" solution.

edit: answer below have been written before lollipop became widespread. nowadays there a handy class direct access linux system calls, makes using jni working file descriptors optional.


quick briefing on descriptors

file descriptors come linux open system call , corresponding open() function in c library. don't need have access file operate on it's descriptor. access checks skipped, crucial information, such access type (read/write/read-and-write etc.) "hardcoded" descriptor , can not changed after created. file descriptors represented non-negative integer numbers, starting 0. numbers local each process , don't have persistent or system-wide meaning, merely distinguish handles files each other given process (0, 1 , 2 traditionally reference stdin, stdout , stderr).

each descriptor represented reference entry in descriptor table, stored in os kernel. there per-process , system-wide limits number of entries in table, close descriptors quickly, unless want attempts open things , create new descriptors fail.

operating on descriptors

in linux there 2 kinds of c library functions , system calls: working names (such readdir(), stat(), chdir(), chown(), open(), link()) , operating on descriptors: getdents, fstat(), fchdir(), fchown(), fchownat(), openat(), linkat() etc. can call these functions , system calls after reading couple of man pages , studying dark jni magic. raise quality of software through roof! (just in case: talking reading , studying, not blindly using jni time).

in java there class working descriptors: java.io.filedescriptor. can used filexxxstream classes , indirectly framework io classes, including memory-mapped , random access files, channels , channel locks. tricky class. because of requirement compatible proprietary os, cross-platform class not expose underlying integer number. can not closed! instead expected close corresponding io classes, (again, compatibility reasons) share same underlying descriptor each other:

fileinputstream filestream1 = new fileinputstream("notes.db"); fileinputstream filestream2 = new fileinputstream(filestream1.getfd()); writablebytechannel achannel = filestream1.getchannel();  // pass filestream1 , achannel methods, written clueless people ...  // surprise them (or surprised them) filestream2.close(); 

there no supported ways integer value out of filedescriptor, can (almost) safely assume, on older os versions there private integer descriptor field, can accessed via reflection.

shooting in foot descriptors

in android framework there specialized class working linux file descriptor: android.os.parcelfiledescriptor. unfortunately, bad filedescriptor. why? 2 reasons:

1) has finalize() method. read it's javadoc learn, means performance. , still has close it, if don't want face sudden io errors.

2) because of being finalizable automatically closed virtual machine once reference class instance goes out of scope. here why having finalize() on framework classes, especially memoryfile mistake on part of framework developers:

public fileoutputstream givemeastream() {   parcelfiledescriptor fd = parcelfiledescriptor.open("myfile", mode_read_only);    return new fileinputstream(fd.getdescriptor()); }  ...  fileinputstream astream = givemeastream();  // enjoy having astream closed during garbage collection 

fortunately, there remedy such horrors: magical dup system call:

public fileoutputstream givemeastream() {   parcelfiledescriptor fd = parcelfiledescriptor.open("myfile", mode_read_only);    return new fileinputstream(fd.dup().getdescriptor()); }  ...  fileinputstream astream = givemeastream();  // safe now...     // kidding! close original parcelfiledescriptor this: public fileoutputstream givemeastreamproperly() {   // use try-with-resources block, because closing things in java hard.   // can employ retrolambda backward compatibility,   // can handle too!         try (parcelfiledescriptor fd = parcelfiledescriptor.open("myfile", mode_read_only)) {      return new fileinputstream(fd.dup().getdescriptor());   } } 

the dup syscall clones integer file descriptor, makes corresponding filedescriptor independent original one. note, passing descriptors across processes not require manual duplication: received descriptors independent source process. passing descriptor of memoryfile (if obtain reflection) does require call dup: having shared memory region destroyed in originating process make inaccessible everyone. furthermore, have either perform dup in native code or keep reference created parcelfiledescriptor until receiver done memoryfile.

giving , receiving descriptors

there 2 ways give , receive file descriptors: having child process inherit creator's descriptors , via interprocess communication.

letting children of process inherit files, pipes , sockets, open creator, common practice in linux, requires forking in native code on android – runtime.exec() , processbuilder close descriptors after creating child process. make sure close unnecessary descriptors too, if choose fork yourself.

the ipc facilities, supporting passing file descriptors on android binder , linux domain sockets.

binder allows give parcelfiledescriptor accepts parcelable objects, including putting them in bundles, returning content providers , passing via aidl calls services.

note, attempts pass bundles descriptors outside of process, including calling startactivityforresult denied system, because timely closing descriptors have been hard. better choices creating contentprovider (which manages descriptor lifecycle you, , publishes files via contentresolver) or writing aidl interface , closing descriptor right after transferred. note, persisting parcelfiledescriptor anywhere not make sense: work until process death , corresponding integer point else, once process recreated.

domain sockets low-level , bit painful use descriptor transfer, compared providers , aidl. are, however, (and documented) option native processes. if forced open files and/or move data around native binaries (which case applications, using root privileges), consider not wasting efforts , cpu resource on intricate communications binaries, instead write open helper. [shameless advert] way, may use the 1 wrote, instead of creating own. [/shameless advert]


answer exact question

i hope, answer have given idea, what's wrong mediastore.mediacolumns.data, , why creating column misnomer on part of android development team.

that said, if still not convinced, want filename at costs, or failed read overwhelming wall of text above, here – have ready-to-go jni function; inspired getting filename file descriptor in c (edit: has reliable java version):

// src/main/jni/fdutil.c jniexport jstring java_com_example_fdutil_getfdpathinternal(jnienv *env, jint descriptor) {   // filesystem name may not fit in path_max, workarounds   // (as resulting strings) prone outofmemoryerror.   // proper solution would, probably, include writing specialized      // charsequence. pain, little gain.   char buf[path_max + 1] = { 0 };    char procfile[25];    sprintf(procfile, "/proc/self/fd/%d", descriptor);    if (readlink(procfile, buf, sizeof(buf)) == -1) {     // descriptor no more, became inaccessible etc.     jclass exclass = (*env) -> findclass(env, "java/io/ioexception");      (*env) -> thrownew(env, exclass, "readlink() failed");      return null;   }    if (buf[path_max] != 0) {     // name on path_max bytes long, caller @ fault     // dealing such tricky descriptors     jclass exclass = (*env) -> findclass(env, "java/io/ioexception");      (*env) -> thrownew(env, exclass, "the path long");      return null;   }    if (buf[0] != '/') {     // name not in filesystem namespace, e.g. socket,     // pipe or     jclass exclass = (*env) -> findclass(env, "java/io/ioexception");      (*env) -> thrownew(env, exclass, "the descriptor not belong file name");      return null;   }    // doing stat on file not give guarantees,   // remain valid, , on android   // inaccessible anyway let's hope   return (*env) -> newstringutf(env, buf); } 

and here class, goes it:

// com/example/fdutil.java public class fdutil {   static {     system.loadlibrary(system.maplibraryname("fdutil"));   }    public static string getfdpath(parcelfiledescriptor fd) throws ioexception {     int intfd = fd.getfd();      if (intfd <= 0)       throw new ioexception("invalid fd");        return getfdpathinternal(intfd);   }    private static native string getfdpathinternal(int fd) throws ioexception; } 

Comments

Popular posts from this blog

cakephp - simple blog with croogo -

How to group boxplot outliers in gnuplot -

bash - Performing variable substitution in a string -