Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong file extension for SimpleTarget<File> #902

Closed
xomyc opened this issue Jan 18, 2016 · 9 comments
Closed

Wrong file extension for SimpleTarget<File> #902

xomyc opened this issue Jan 18, 2016 · 9 comments
Labels

Comments

@xomyc
Copy link

xomyc commented Jan 18, 2016

I'm trying to save file, picked by user. With jpegs everything is ok, but if user picks png or gif image, resulting resource name contains extension .0 instead of .png or .gif

here is the code:

Glide.with(this).load(photoUri).downloadOnly(new SimpleTarget<File>() {
    @Override public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {
        pickedPhotoPath = resource.getAbsolutePath();
    }
});
@TWiStErRob
Copy link
Collaborator

downloadOnly gives you the File from SOURCE cache and DiskLruCache has versioning built in hence the .0 extension (first version). Depending on what you do with pickedPhotoPath I suggest copying (do not rename) it to its final place (you can get the content type or file name from the photoUri), or keeping it in memory via:

Glide
    .with(this)
    .load(uri)
    .asBitmap()
    .toBytes(CompressFormat.JPEG, 80) // you have to pick a format with this solution
    .format(DecodeFormat.PREFER_ARGB_8888)
    .atMost()
    .override(MAX_IMAGE_SIZE, MAX_IMAGE_SIZE)
    .diskCacheStrategy(DiskCacheStrategy.NONE)
    .skipMemoryCache(true)
    .into(new SimpleTarget<byte[]>() {

@SaeedZhiany
Copy link

@TWiStErRob I write this code:

public static synchronized void saveImage(Context context, String imageUrl) {
    Glide.with(context.getApplicationContext())
        .load(imageUrl)
        .downloadOnly(new SimpleTarget<File>() {
            @Override
            public void onResourceReady(File src, GlideAnimation<? super File> glideAnimation) {
                try {
                    File dst = new File(APP_GALLERY_IMAGE_PATH + "/" + src.getName());
                    if(!dst.mkdirs()) {
                        throw new IOException("");
                    }
                    InputStream in = new FileInputStream(src);
                    OutputStream out = new FileOutputStream(dst);

                    // Transfer bytes from in to out
                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = in.read(buf)) > 0) {
                        out.write(buf, 0, len);
                    }
                    in.close();
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
}

but I also get wrong extension. also the created thing is directory not File. and is 0KB

@TWiStErRob
Copy link
Collaborator

Java does what you ask it to do. You create a folder with the name of the file Glide has given you.

To ensure that the folder is ready to receive new files:

File gallery = new File(APP_GALLERY_IMAGE_PATH);
if (!gallery.mkdirs() && (!gallery.exists() || !gallery.isDirectory())) {
    throw new IOException("Invalid gallery path: " + gallery);
}

then, to create a file with a meaningful name either:

String ext = android.webkit.MimeTypeMap.getFileExtensionFromUrl(imageUrl);
File dst = new File(gallery, src.getName() + "." + ext);

to get the extension from the original url, but use the cache hash file name; or

String name = android.webkit.URLUtil.guessFileName(imageUrl, null, null);
File dst = new File(gallery, name);

to get the full original name from the url.


Also never do I/O on the UI thread, fire up an AsyncTask or some other background thread to save the file from onResourceReady, it may be long code but users will appreciate if the app is responsive. It's also worth using a buffer of size 16*1024.

@SaeedZhiany
Copy link

I use downloadOnly(Target) method. according to documentation I can do it, right?
what size of buffer is better?

@TWiStErRob
Copy link
Collaborator

If the file access is not buffered by FileInputStream you may have 1k reads for a megabyte large file, with bigger buffer you'll have less actual I/O accesses. It should be measured, but I think 4-64k is reasonable, 16 is a good guess.

Your downloadOnly usage is correct, but expensive code in onResourceReady blocks the UI thread which means no input and no draw = degraded user experience. So it's better to delegate to a background thread. An inner class is not necessarily a background thread.

@SaeedZhiany
Copy link

and another thing. the parameter "imageUrl" is something like this
http://hostname//ServiceName?id=1
I don't think
String ext = android.webkit.MimeTypeMap.getFileExtensionFromUrl(imageUrl);
can help me. are you have any other suggestion?

@TWiStErRob
Copy link
Collaborator

I gave you two options, you can write any code based on top of that to handle edge cases. You know your urls and types of files. It is possible that appending ".jpg" to the name is enough for you, or you can even peek at the first few bytes of the file to determine the type with new ImageHeaderParser(in).getType() (you'll have to rewind or close and reopen in after that!). You may even have some metadata (like mime type or original filename) from the source that gave you imageUrl. The question of saving a file with a proper name is really broad, the requirements are up to you.

@SaeedZhiany
Copy link

I changed my code:

public static synchronized void saveImage(Context context, final String imageUrl) {
    downloadImage downloadImage = new downloadImage(context, imageUrl);
    downloadImage.execute();
}

private static class downloadImage extends AsyncTask<Void, Void, Void>{

    private Context context;
    private String imageUrl;

    public downloadImage(Context context, String imageUrl){
        this.context = context;
        this.imageUrl = imageUrl;
    }

    @Override
    protected Void doInBackground(Void... params) {
        Glide.with(context.getApplicationContext())
            .load(imageUrl)
            .downloadOnly(new SimpleTarget<File>() {
                @Override
                public void onResourceReady(File src, GlideAnimation<? super File> glideAnimation) {
                    try {
                        File gallery = new File(APP_GALLERY_IMAGE_PATH);
                        if (!gallery.mkdirs() && (!gallery.exists() || !gallery.isDirectory())) {
                            throw new IOException("Invalid gallery path: " + APP_GALLERY_IMAGE_PATH);
                        }
                        Random random = new Random();
                        String ext = android.webkit.MimeTypeMap.getFileExtensionFromUrl(imageUrl);
                        File dst =
                                new File(gallery,
                                        String.valueOf(random.nextInt()) + ".jpg");
                        Log.w("saeed", dst.getName());
                        if(!dst.createNewFile()) {
                            throw new IOException("");
                        }
                        InputStream in = new FileInputStream(src);
                        OutputStream out = new FileOutputStream(dst);

                        // Transfer bytes from in to out
                        byte[] buf = new byte[1024*16];
                        int len;
                        while ((len = in.read(buf)) > 0) {
                            out.write(buf, 0, len);
                        }
                        in.close();
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        return null;
    }
}

in rumtime I get this fatal exception:

01-30 00:29:20.068  11685-11728/com.kingbabak.digitalmarket E/AndroidRuntime﹕ in writeCrashedAppName, pkgName :com.kingbabak.digitalmarket
01-30 00:29:20.068  11685-11728/com.kingbabak.digitalmarket E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
Process: com.kingbabak.digitalmarket, PID: 11685
java.lang.RuntimeException: An error occured while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:300)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
        at java.util.concurrent.FutureTask.run(FutureTask.java:242)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:841)
 Caused by: java.lang.IllegalArgumentException: You must call this method on the main thread
        at com.bumptech.glide.util.Util.assertMainThread(Util.java:135)
        at com.bumptech.glide.GenericRequestBuilder.into(GenericRequestBuilder.java:642)
        at com.bumptech.glide.GenericTranscodeRequest.downloadOnly(GenericTranscodeRequest.java:89)
        at com.bumptech.glide.DrawableTypeRequest.downloadOnly(DrawableTypeRequest.java:96)
        at util.DirectoryUtil$downloadImage.doInBackground(DirectoryUtil.java:51) // here 
        at util.DirectoryUtil$downloadImage.doInBackground(DirectoryUtil.java:37)
        at android.os.AsyncTask$2.call(AsyncTask.java:288)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:841)

@TWiStErRob
Copy link
Collaborator

The contents of onResourceReady should be delegated, not the Glide load:

public static synchronized void saveImage(final Context context, final String imageUrl) {
    Glide.with(context.getApplicationContext())
         .load(imageUrl)
         .downloadOnly(new SimpleTarget<File>() {
             @Override public void onResourceReady(File src, GlideAnimation<? super File> glideAnimation) {
                 new GalleryFileSaver(context, imageUrl, src).execute();
             }
         });
}

private static class GalleryFileSaver extends AsyncTask<Void, Void, File> {
    private final Context context;
    private final String imageUrl;
    private final File src;
    public GalleryFileSaver(Context context, String imageUrl, File src) {
        this.context = context;
        this.imageUrl = imageUrl;
        this.src = src;
    }

    @Override protected File doInBackground(Void... params) {
        try {
            dst = new File(getGalleryFolder(), getTargetFileName());
            copy(new FileInputStream(src), new FileOutputStream(dst));
        } catch (IOException e) {
            dst = null;
            e.printStackTrace();
        }
        return dst;
    }
    @Override protected void onPostExecute(File dst) {
        String message = dst != null? "Saved file to " + dst : "Failed to save file from " + src;
        Toast.makeText(context, message, Toast.LENGTH_LONG).show();
    }
    private void copy(InputStream in, OutputStream out) throws IOException {
        try {
            byte[] buf = new byte[1024 * 16];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        } finally {
            try { in.close(); } catch (IOException ignore) { }
            try { out.close(); } catch (IOException ignore) { }
        }
    }
    private String getTargetFileName() throws IOException {
        Random random = new Random();
        String ext = android.webkit.MimeTypeMap.getFileExtensionFromUrl(imageUrl);
        String name = String.valueOf(random.nextInt()) + ".jpg";
        Log.w("saeed", name);
        return name;
    }
    private File getGalleryFolder() throws IOException {
        File gallery = new File(APP_GALLERY_IMAGE_PATH);
        if (!gallery.mkdirs() && (!gallery.exists() || !gallery.isDirectory())) {
            throw new IOException("Invalid gallery path: " + gallery);
        }
        return gallery;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants