使用Picasso从URL保存图像?

29

我正在尝试使用API Picasso保存一张图片。为此,我正尝试使用Target来保存,但我无法做好这项工作。

我该怎么做?

正在尝试中。

//save image
    public static void imageDownload(Context ctx){
        Picasso.with(ctx)
                .load("http://blog.concretesolutions.com.br/wp-content/uploads/2015/04/Android1.png")
                .into(getTarget("http://blog.concretesolutions.com.br/wp-content/uploads/2015/04/Android1.png"));
    }

    //target to save
    private static Target getTarget(final String url){
        Target target = new Target(){

            @Override
            public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //Log.i("PRODUTOS_FOLDER", CreateAppFolder.getProdutosFolder());
                        File file = new File(Environment.getExternalStorageDirectory() + url);

                        try {
                            file.createNewFile();
                            FileOutputStream ostream = new FileOutputStream(file);
                            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, ostream);
                            ostream.flush();
                            ostream.close();
                        }
                        catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();

            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) {

            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {

            }
        };
        return target;
    }

例外

java.io.IOException: open failed: ENOENT (No such file or directory)

1
你的问题到底是什么,回调函数不起作用,还是位图没有保存,或者其他什么问题? - Vasyl Glodan
1
@VasylGlodan 抛出异常 java.io.IOException: open failed: ENOENT (No such file or directory) - FernandoPaiva
嗯,我不确定,但是您的文件路径应该像这样/storage/emulated/0/http://blog.concretesolutions.com.br/wp-content/uploads/2015/04/Android1.png,系统试图找到名为/storage/emulated/0/http://blog.concretesolutions.com.br/wp-content/uploads/2015/04/的目录,但是没有这样的目录。尝试从文件名中删除所有特殊字符。 - Vasyl Glodan
@FernandoPaiva遇到了相同的问题。尝试将您的文件名命名为其他内容,问题在于当您想要以URL创建文件名时会出现问题。 尝试使用当前时间或其他内容。 - Mahdi Giveie
5个回答

30

问题已经解决,现在正常工作!

我做了

//save image
    public static void imageDownload(Context ctx, String url){
        Picasso.with(ctx)
                .load("http://blog.concretesolutions.com.br/wp-content/uploads/2015/04/Android1.png")
                .into(getTarget(url));
    }

    //target to save
    private static Target getTarget(final String url){
        Target target = new Target(){

            @Override
            public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
                new Thread(new Runnable() {

                    @Override
                    public void run() {

                        File file = new File(Environment.getExternalStorageDirectory().getPath() + "/" + url);
                        try {
                            file.createNewFile();
                            FileOutputStream ostream = new FileOutputStream(file);
                            bitmap.compress(Bitmap.CompressFormat.JPEG, 80, ostream);
                            ostream.flush();
                            ostream.close();
                        } catch (IOException e) {
                            Log.e("IOException", e.getLocalizedMessage());
                        }
                    }
                }).start();

            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) {

            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {

            }
        };
        return target;
    }

它给我一个错误:打开失败:EACCES(权限被拒绝)。在清单文件中,我已经声明了<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />。 - AEMLoviji
@AEMLoviji 首先使用Environment.getExternalStorageState == Environment.MEDIA_MOUNTED检查外部存储状态。 - NineToeNerd
为什么不将图像网址进行哈希处理并将其转换为长整数,以便生成文件名? - zyamys

3

我看到有两个可能的问题:

  1. 尝试在您的清单中没有写入权限的情况下保存到外部存储器
  2. 尝试更改文件名,使其不是整个网址,这可能是由于网址中的字符不是有效的文件名字符而引起的问题。

我使用了这段代码。在我的Android清单文件中,我声明了权限:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />,文件名为“simpleFileName”。现在当我尝试保存它时,它会给我一个错误:java.io.IOException: open failed:EACCES(Permission denied)。我该怎么办? - AEMLoviji

1
我改进了这个解决方案,增加了权限和一个按钮来加载和保存图像,并且 PhotoLoader 类保持不变!
private static final String[] STORAGE_PERMISSIONS = { Manifest.permission.WRITE_EXTERNAL_STORAGE};
    ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = (ImageView) findViewById(R.id.imageView);

        verifyPermissions();
    }
    public void save(View view)
    {
        Picasso.with(this)
                .load("https://www.w3schools.com/howto/img_fjords.jpg")
                .into(new PhotoLoader("myImg.jpg" , imageView));
    }

    public void verifyPermissions()
    {
        // This will return the current Status
        int permissionExternalMemory = ActivityCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if(permissionExternalMemory != PackageManager.PERMISSION_GRANTED)
        {
            // If permission not granted then ask for permission real time.
            ActivityCompat.requestPermissions(MainActivity.this,STORAGE_PERMISSIONS,1);
        }
    }

1

我的Kotlin解决方案,将文件存储到内部存储中而无需用户权限:

由于我需要一次下载多个图像,所以必须创建一个名为shareTargetsArray的可变列表<Target?>,并将它们填充到多个目标中,否则垃圾收集器会除了最后一个之外删除它们。只需在完成后清理该数组即可。

    private var shareTargetsArray: MutableList<Target?> = ArrayList()

    fun downloadImage(context: Context, url: String?, slug: String) {

        val shareTarget = object : Target {
            override fun onBitmapLoaded(bitmap: Bitmap, from: LoadedFrom?) {

                    val contextWrapper = ContextWrapper(context)
                    val directory: File =
                        contextWrapper.getDir("customDirectory", Context.MODE_PRIVATE)
                    val file = File(directory, slug + ".png")

                    var outputStream: FileOutputStream? = null

                    try {
                        outputStream = FileOutputStream(file)

                        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
                        outputStream.flush()
                    } catch (e: IOException) {
                        e.printStackTrace()
                    } finally {
                        try {
                            outputStream!!.close()
                        } catch (e: IOException) {
                            e.printStackTrace()
                        }
                    }
                }

            override fun onBitmapFailed(e: java.lang.Exception?, errorDrawable: Drawable?) {}

            override fun onPrepareLoad(placeHolderDrawable: Drawable?) {}
        }

        shareTargetsArray.add(shareTarget)
        Picasso.get().load(url).into(shareTarget)
    }
}

文件将下载到 /data/data/com.***.***/customDirectory/ 目录下,您可以使用 View > Tool Windows > Device File Explorer 进行查看。

0

我认为你需要检查一下是否实际上在请求权限。 在 Android 中,权限从版本 6.0 开始是动态的。要么你必须在运行时请求它,要么将目标 SDK 版本降级到 22。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接