Android에서 이미지를 다운로드하고 저장하는 방법


답변:


224

2015 년 12 월 30 일 편집-이미지 다운로드에 대한 궁극적 인 가이드


마지막 주요 업데이트 : 2016 년 3 월 31 일


TL; DR 일명 그만 얘기해, 그냥 코드를 줘 !!

이 게시물의 맨 아래로 건너 뛰고 BasicImageDownloader( 여기에서 javadoc 버전 ) 프로젝트에 복사 하고 OnImageLoaderListener인터페이스를 구현하면 완료됩니다.

참고 :에서 BasicImageDownloader가능한 오류를 처리하고 문제가 발생할 경우 앱이 다운되는 것을 방지 하지만 다운로드 한 .NET에서 사후 처리 (예 : 크기 축소)를 수행하지 않습니다 Bitmaps.


이 게시물은 많은 관심을 받았기 때문에 사람들이 더 이상 사용되지 않는 기술, 나쁜 프로그래밍 관행을 사용하거나 어리석은 일을하지 않도록 완전히 재 작업하기로 결정했습니다. 모든 SSL 인증서를 수락합니다.

필자는 자체 다운로더 구현, Android 내장 및 DownloadManager일부 인기있는 오픈 소스 라이브러리를 사용하여 이미지를 다운로드 (및 저장)하는 방법을 보여주는 "Image Downloader"라는 데모 프로젝트를 만들었습니다 . 전체 소스 코드를 보거나 GitHub 에서 프로젝트 다운로드 할 수 있습니다 .

참고 : 아직 SDK 23+ (Marshmallow)에 대한 권한 관리를 조정하지 않았으므로 프로젝트는 SDK 22 (Lollipop)를 대상으로합니다.

내에서 결론 이 게시물의 끝에 나는 공유 할 것입니다 내 소견 내가 언급 한 이미지 다운로드의 각각의 특정 방법에 대한 적절한 사용 사례에 대한합니다.

자체 구현부터 시작하겠습니다 (게시물 끝에서 코드를 찾을 수 있음). 우선, 이것이 기본 ImageDownloader이고 그게 다입니다. 그것이하는 일은 주어진 URL에 연결하고, 데이터를 읽고, 그것을으로 디코딩하려고 시도하고 Bitmap, OnImageLoaderListener적절한 경우 인터페이스 콜백을 트리거하는 것 입니다. 이 접근 방식의 장점은 간단하며 진행 상황에 대한 명확한 개요를 제공합니다. 메모리 / 디스크 캐시를 유지하는 데 신경 쓰지 않으면 서 일부 이미지를 다운로드 / 표시 및 저장하는 것이 필요한 경우 좋은 방법입니다.

참고 : 이미지가 큰 경우 축소 해야 할 수 있습니다 .

-

Android DownloadManager 는 시스템에서 다운로드를 처리 할 수있는 방법입니다. 실제로 이미지뿐만 아니라 모든 종류의 파일을 다운로드 할 수 있습니다. 다운로드가 조용하고 사용자에게 표시되지 않도록하거나 사용자가 알림 영역에서 다운로드를 볼 수 있도록 할 수 있습니다. BroadcastReceiver다운로드가 완료된 후 알림을 받도록 등록 할 수도 있습니다 . 설정은 매우 간단합니다. 샘플 코드는 링크 된 프로젝트를 참조하십시오.

(가) 사용 DownloadManager당신은 또한 이미지를 표시하려는 경우 읽고 바로 다운로드 설정하는 대신 저장된 파일을 디코딩해야하는 것 때문에, 일반적으로 좋은 생각이 아니다 BitmapImageView. 은 DownloadManager당신이 다운로드 진행 상황을 추적하는 앱도 어떤 API를 제공하지 않습니다.

-

이제 훌륭한 물건 인 도서관을 소개합니다. 메모리 / 디스크 캐시 생성 및 관리, 이미지 크기 조정, 이미지 변환 등을 포함하여 이미지를 다운로드하고 표시하는 것 이상을 수행 할 수 있습니다.

Google에서 만들고 공식 문서로 다루는 강력한 라이브러리 인 Volley 부터 시작하겠습니다 . Volley는 이미지를 전문으로하지 않는 범용 네트워킹 라이브러리이지만 이미지 관리를위한 강력한 API를 제공합니다.

Volley 요청을 관리하기 위해 Singleton 클래스 를 구현해야합니다 .

ImageView을 Volley 's 로 바꾸고 싶을 수 있으므로 NetworkImageView다운로드는 기본적으로 한 줄짜리가됩니다.

((NetworkImageView) findViewById(R.id.myNIV)).setImageUrl(url, MySingleton.getInstance(this).getImageLoader());

더 많은 제어가 필요한 경우 ImageRequestVolley 를 사용하여 만드는 것은 다음과 같습니다 .

     ImageRequest imgRequest = new ImageRequest(url, new Response.Listener<Bitmap>() {
             @Override
             public void onResponse(Bitmap response) {
                    //do stuff
                }
            }, 0, 0, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.ARGB_8888, 
             new Response.ErrorListener() {
             @Override
             public void onErrorResponse(VolleyError error) {
                   //do stuff
                }
            });

Volley는 오류 VolleyError의 정확한 원인을 파악하는 데 도움이 되는 클래스를 제공하여 뛰어난 오류 처리 메커니즘을 제공한다는 점을 언급 할 가치가 있습니다. 앱이 많은 네트워킹을 수행하고 이미지를 관리하는 것이 주요 목적이 아니라면 Volley가 적합합니다.

-

Square의 Picasso 는 모든 이미지 로딩 작업을 수행하는 잘 알려진 라이브러리입니다. Picasso를 사용하여 이미지를 표시하는 것은 다음과 같이 간단합니다.

 Picasso.with(myContext)
       .load(url)
       .into(myImageView); 

기본적으로 Picasso는 디스크 / 메모리 캐시를 관리하므로 걱정할 필요가 없습니다. 더 많은 제어를 위해 Target인터페이스를 구현하고 이를 사용하여 이미지를로드 할 수 있습니다. 그러면 Volley 예제와 유사한 콜백이 제공됩니다. 예제는 데모 프로젝트를 확인하십시오.

Picasso를 사용하면 다운로드 한 이미지에 변환을 적용 할 수 있으며 해당 API를 확장하는 다른 라이브러리 도 있습니다. 또한 RecyclerView/ ListView/ 에서 잘 작동합니다 GridView.

-

Universal Image Loader 는 이미지 관리 목적을 제공하는 또 다른 매우 인기있는 라이브러리입니다. ImageLoader한 줄의 코드로 이미지를 다운로드하는 데 사용할 수있는 전역 인스턴스 (초기화되면)가있는 자체 를 사용합니다.

  ImageLoader.getInstance().displayImage(url, myImageView);

다운로드 진행 상황을 추적하거나 다운로드 한 파일에 액세스하려는 경우 Bitmap:

 ImageLoader.getInstance().displayImage(url, myImageView, opts, 
 new ImageLoadingListener() {
     @Override
     public void onLoadingStarted(String imageUri, View view) {
                     //do stuff
                }

      @Override
      public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
                   //do stuff
                }

      @Override
      public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                   //do stuff
                }

      @Override
      public void onLoadingCancelled(String imageUri, View view) {
                   //do stuff
                }
            }, new ImageLoadingProgressListener() {
      @Override
      public void onProgressUpdate(String imageUri, View view, int current, int total) {
                   //do stuff
                }
            });

opts이 예제 의 인수는 DisplayImageOptions객체입니다. 자세한 내용은 데모 프로젝트를 참조하십시오.

Volley와 마찬가지로 UIL은 FailReason다운로드 실패시 무엇이 잘못되었는지 확인할 수 있는 클래스를 제공합니다 . 기본적으로 UIL은 명시 적으로 지정하지 않으면 메모리 / 디스크 캐시를 유지합니다.

참고 : 저자는 2015 년 11 월 27 일 현재 더 이상 프로젝트를 유지하지 않는다고 언급했습니다.하지만 많은 기여자가 있기 때문에 Universal Image Loader가 계속 유지되기를 바랍니다.

-

Facebook의 Fresco 는 최신 버전이며 (IMO)는 이미지 관리를 새로운 수준으로 끌어 올리는 가장 진보 된 라이브러리입니다 Bitmaps. Java 힙 (Lollipop 이전)에서 애니메이션 형식점진적 JPEG 스트리밍 지원에 이르기까지 .

Fresco의 아이디어와 기술에 대한 자세한 내용은 이 게시물을 참조하십시오 .

기본적인 사용법은 아주 간단합니다. 클래스 Fresco.initialize(Context);에서 선호하는 한 번만 호출 하면 Application됩니다. Fresco를 두 번 이상 초기화하면 예기치 않은 동작과 OOM 오류가 발생할 수 있습니다.

Fresco는 Drawees를 사용 하여 이미지를 표시하므로 다음과 같이 생각할 수 있습니다 ImageView.

    <com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/drawee"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    fresco:fadeDuration="500"
    fresco:actualImageScaleType="centerCrop"
    fresco:placeholderImage="@drawable/placeholder_grey"
    fresco:failureImage="@drawable/error_orange"
    fresco:placeholderImageScaleType="fitCenter"
    fresco:failureImageScaleType="centerInside"
    fresco:retryImageScaleType="centerCrop"
    fresco:progressBarImageScaleType="centerInside"
    fresco:progressBarAutoRotateInterval="1000"
    fresco:roundAsCircle="false" />

보시다시피 변환 옵션을 포함한 많은 항목이 이미 XML로 정의되어 있으므로 이미지를 표시하는 데 필요한 작업은 한 줄만 있으면됩니다.

 mDrawee.setImageURI(Uri.parse(url));

Fresco는 확장 된 사용자 정의 API를 제공하는데, 상황에서는 매우 복잡 할 수 있으며 사용자가 문서를주의 깊게 읽어야합니다 (예, 때로는 RTFM 이 필요함 ).

샘플 프로젝트에 프로그레시브 JPEG 및 애니메이션 이미지에 대한 예제를 포함했습니다.


결론- "대단한 것들을 배웠습니다. 이제 무엇을 사용해야합니까?"

다음 텍스트는 내 개인적인 의견을 반영하며 가정으로 간주해서는 안됩니다.

  • 일부 이미지 만 다운로드 / 저장 / 표시 Recycler-/Grid-/ListView할 필요가 있고 이미지를 a 에서 사용할 계획 이없고 전체 이미지를 표시 할 필요가없는 경우 BasicImageDownloader 가 사용자의 요구에 맞아야합니다.
  • 앱이 사용자 또는 자동화 된 작업의 결과로 이미지 (또는 기타 파일)를 저장하고 이미지를 자주 표시 할 필요가없는 경우 Android DownloadManager를 사용 하세요 .
  • 앱이 많은 네트워킹을 수행하고 JSON데이터를 전송 / 수신 하고 이미지와 함께 작동하지만 앱의 주요 목적이 아닌 경우 Volley를 사용하십시오 .
  • 앱이 이미지 / 미디어에 중점을두고 있으며 이미지에 일부 변형을 적용하고 복잡한 API를 사용하고 싶지 않습니다. Picasso를 사용하십시오 (참고 : 중간 다운로드 상태를 추적하는 API를 제공하지 않음) 또는 Universal Image 짐을 싣는 사람
  • 앱이 이미지에 관한 것이라면 애니메이션 형식 표시와 같은 고급 기능이 필요하고 문서를 읽을 준비가 된 경우 Fresco를 사용하십시오 .

그것을 놓친 경우 데모 프로젝트에 대한 Github 링크 .


그리고 여기에 BasicImageDownloader.java

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Set;

public class BasicImageDownloader {

    private OnImageLoaderListener mImageLoaderListener;
    private Set<String> mUrlsInProgress = new HashSet<>();
    private final String TAG = this.getClass().getSimpleName();

    public BasicImageDownloader(@NonNull OnImageLoaderListener listener) {
        this.mImageLoaderListener = listener;
    }

    public interface OnImageLoaderListener {
        void onError(ImageError error);      
        void onProgressChange(int percent);
        void onComplete(Bitmap result);
    }


    public void download(@NonNull final String imageUrl, final boolean displayProgress) {
        if (mUrlsInProgress.contains(imageUrl)) {
            Log.w(TAG, "a download for this url is already running, " +
                    "no further download will be started");
            return;
        }

        new AsyncTask<Void, Integer, Bitmap>() {

            private ImageError error;

            @Override
            protected void onPreExecute() {
                mUrlsInProgress.add(imageUrl);
                Log.d(TAG, "starting download");
            }

            @Override
            protected void onCancelled() {
                mUrlsInProgress.remove(imageUrl);
                mImageLoaderListener.onError(error);
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                mImageLoaderListener.onProgressChange(values[0]);
            }

        @Override
        protected Bitmap doInBackground(Void... params) {
            Bitmap bitmap = null;
            HttpURLConnection connection = null;
            InputStream is = null;
            ByteArrayOutputStream out = null;
            try {
                connection = (HttpURLConnection) new URL(imageUrl).openConnection();
                if (displayProgress) {
                    connection.connect();
                    final int length = connection.getContentLength();
                    if (length <= 0) {
                        error = new ImageError("Invalid content length. The URL is probably not pointing to a file")
                                .setErrorCode(ImageError.ERROR_INVALID_FILE);
                        this.cancel(true);
                    }
                    is = new BufferedInputStream(connection.getInputStream(), 8192);
                    out = new ByteArrayOutputStream();
                    byte bytes[] = new byte[8192];
                    int count;
                    long read = 0;
                    while ((count = is.read(bytes)) != -1) {
                        read += count;
                        out.write(bytes, 0, count);
                        publishProgress((int) ((read * 100) / length));
                    }
                    bitmap = BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.size());
                } else {
                    is = connection.getInputStream();
                    bitmap = BitmapFactory.decodeStream(is);
                }
            } catch (Throwable e) {
                if (!this.isCancelled()) {
                    error = new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION);
                    this.cancel(true);
                }
            } finally {
                try {
                    if (connection != null)
                        connection.disconnect();
                    if (out != null) {
                        out.flush();
                        out.close();
                    }
                    if (is != null)
                        is.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return bitmap;
        }

            @Override
            protected void onPostExecute(Bitmap result) {
                if (result == null) {
                    Log.e(TAG, "factory returned a null result");
                    mImageLoaderListener.onError(new ImageError("downloaded file could not be decoded as bitmap")
                            .setErrorCode(ImageError.ERROR_DECODE_FAILED));
                } else {
                    Log.d(TAG, "download complete, " + result.getByteCount() +
                            " bytes transferred");
                    mImageLoaderListener.onComplete(result);
                }
                mUrlsInProgress.remove(imageUrl);
                System.gc();
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    public interface OnBitmapSaveListener {
        void onBitmapSaved();
        void onBitmapSaveError(ImageError error);
    }


    public static void writeToDisk(@NonNull final File imageFile, @NonNull final Bitmap image,
                               @NonNull final OnBitmapSaveListener listener,
                               @NonNull final Bitmap.CompressFormat format, boolean shouldOverwrite) {

    if (imageFile.isDirectory()) {
        listener.onBitmapSaveError(new ImageError("the specified path points to a directory, " +
                "should be a file").setErrorCode(ImageError.ERROR_IS_DIRECTORY));
        return;
    }

    if (imageFile.exists()) {
        if (!shouldOverwrite) {
            listener.onBitmapSaveError(new ImageError("file already exists, " +
                    "write operation cancelled").setErrorCode(ImageError.ERROR_FILE_EXISTS));
            return;
        } else if (!imageFile.delete()) {
            listener.onBitmapSaveError(new ImageError("could not delete existing file, " +
                    "most likely the write permission was denied")
                    .setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
            return;
        }
    }

    File parent = imageFile.getParentFile();
    if (!parent.exists() && !parent.mkdirs()) {
        listener.onBitmapSaveError(new ImageError("could not create parent directory")
                .setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
        return;
    }

    try {
        if (!imageFile.createNewFile()) {
            listener.onBitmapSaveError(new ImageError("could not create file")
                    .setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
            return;
        }
    } catch (IOException e) {
        listener.onBitmapSaveError(new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION));
        return;
    }

    new AsyncTask<Void, Void, Void>() {

        private ImageError error;

        @Override
        protected Void doInBackground(Void... params) {
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(imageFile);
                image.compress(format, 100, fos);
            } catch (IOException e) {
                error = new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION);
                this.cancel(true);
            } finally {
                if (fos != null) {
                    try {
                        fos.flush();
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return null;
        }

        @Override
        protected void onCancelled() {
            listener.onBitmapSaveError(error);
        }

        @Override
        protected void onPostExecute(Void result) {
            listener.onBitmapSaved();
        }
    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }

public static Bitmap readFromDisk(@NonNull File imageFile) {
    if (!imageFile.exists() || imageFile.isDirectory()) return null;
    return BitmapFactory.decodeFile(imageFile.getAbsolutePath());
}

public interface OnImageReadListener {
    void onImageRead(Bitmap bitmap);
    void onReadFailed();
}

public static void readFromDiskAsync(@NonNull File imageFile, @NonNull final OnImageReadListener listener) {
    new AsyncTask<String, Void, Bitmap>() {
        @Override
        protected Bitmap doInBackground(String... params) {
            return BitmapFactory.decodeFile(params[0]);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (bitmap != null)
                listener.onImageRead(bitmap);
            else
                listener.onReadFailed();
        }
    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imageFile.getAbsolutePath());
}

    public static final class ImageError extends Throwable {

        private int errorCode;
        public static final int ERROR_GENERAL_EXCEPTION = -1;
        public static final int ERROR_INVALID_FILE = 0;
        public static final int ERROR_DECODE_FAILED = 1;
        public static final int ERROR_FILE_EXISTS = 2;
        public static final int ERROR_PERMISSION_DENIED = 3;
        public static final int ERROR_IS_DIRECTORY = 4;


        public ImageError(@NonNull String message) {
            super(message);
        }

        public ImageError(@NonNull Throwable error) {
            super(error.getMessage(), error.getCause());
            this.setStackTrace(error.getStackTrace());
        }

        public ImageError setErrorCode(int code) {
            this.errorCode = code;
            return this;
        }

        public int getErrorCode() {
            return errorCode;
        }
      }
   }

사진을 byte []로 제공하는 onPictureTaken () 콜백은 어떻습니까? 카메라에서 바로 해당 사진의 URL을 얻을 수 있습니까? 아니면 안드로이드에서 내장 인 텐트를 사용하지 않고 카메라로 찍은 사진을 저장하는 유일한 방법은 기본적인 오래된 outputStream ()입니까? onPictureTaken 이후에 할 자연스러운 일은 물론 저장하는 것이기 때문에 이상하게 보입니다. 그렇게하는 것에 대한 특별한 지원이 없습니까?
Tombola

2
@Tombola 안녕하세요! 이 게시물은 웹에서 사진을 다운로드하는 것에 관한 것입니다. 그러나 귀하의 질문에 대답하려면 (내가 이해하는 한) 카메라 사진을 저장하는 일반적인 방법은 Cursor( onActivityResult()방법에서) 에서 경로를 얻은 다음 Bitmap해당 경로를 사용하여 만드는 것 입니다. 그리고 예, 이 이미지를 SD에 저장하려는 경우 a FileOutputStream및 a 를 사용하지 않아도됩니다 ByteArrayOutputStream.
Droidman

@BartBurg이 질문은 이미지 다운로드 및 저장에 관한 것입니다. 하지만 어느 시점에서 당신 말이 맞습니다. 쓰기 방법이 있기 때문에 완전성을 위해 읽기 방법도 있어야합니다. 조만간이 게시물의 다음 업데이트에서 추가하겠습니다.
Droidman

이 BasicImageDownloader를 사용하여 예제를 제공 할 수 있습니까?
Jaydev

1
@JaydevKalivarapu GitHub에서 데모 앱을 확인하십시오 ( 예제를 포함하는 소스 클래스 )
Droidman

35

나는 방금이 문제를 해결하면서 왔으며 다운로드 할 수있는 완전한 코드를 공유하고 sdcard에 저장하고 (그리고 파일 이름을 숨기고) 이미지를 검색하고 마지막으로 이미지가 이미 있는지 확인합니다. URL은 데이터베이스에서 가져 오므로 파일 이름은 id를 사용하여 쉽게 고유 할 수 있습니다.

첫 번째 다운로드 이미지

   private class GetImages extends AsyncTask<Object, Object, Object> {
    private String requestUrl, imagename_;
    private ImageView view;
    private Bitmap bitmap ; 
      private FileOutputStream fos;
    private GetImages(String requestUrl, ImageView view, String _imagename_) {
        this.requestUrl = requestUrl;
        this.view = view;
        this.imagename_ = _imagename_ ;
    }

    @Override
    protected Object doInBackground(Object... objects) {
        try {
            URL url = new URL(requestUrl);
            URLConnection conn = url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
        } catch (Exception ex) {
        }
        return null;
    }

    @Override
    protected void onPostExecute(Object o) { 
        if(!ImageStorage.checkifImageExists(imagename_))
        { 
                view.setImageBitmap(bitmap);
                ImageStorage.saveToSdCard(bitmap, imagename_); 
            }  
        }  
   }

그런 다음 파일을 저장하고 검색하기위한 클래스를 만듭니다.

  public class ImageStorage {


public static String saveToSdCard(Bitmap bitmap, String filename) {

    String stored = null;

    File sdcard = Environment.getExternalStorageDirectory() ; 

    File folder = new File(sdcard.getAbsoluteFile(), ".your_specific_directory");//the dot makes this directory hidden to the user
    folder.mkdir(); 
    File file = new File(folder.getAbsoluteFile(), filename + ".jpg") ;
    if (file.exists())
        return stored ;

    try {
        FileOutputStream out = new FileOutputStream(file);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
        out.flush();
        out.close();
        stored = "success";
    } catch (Exception e) {
        e.printStackTrace();
    }
     return stored;
   }

public static File getImage(String imagename) {

        File mediaImage = null;
        try {
            String root = Environment.getExternalStorageDirectory().toString();
            File myDir = new File(root);
            if (!myDir.exists())
                return null;

            mediaImage = new File(myDir.getPath() + "/.your_specific_directory/"+imagename);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return mediaImage;
    }
public static boolean checkifImageExists(String imagename)
{
    Bitmap b = null ;
    File file = ImageStorage.getImage("/"+imagename+".jpg");
    String path = file.getAbsolutePath();

    if (path != null)
        b = BitmapFactory.decodeFile(path); 

    if(b == null ||  b.equals(""))
    {
        return false ;
    }
    return true ;
}
  }

그런 다음 이미지에 액세스하려면 먼저 이미지가 이미 있는지 확인한 다음 다운로드하십시오.

          if(ImageStorage.checkifImageExists(imagename))
            {
                File file = ImageStorage.getImage("/"+imagename+".jpg"); 
                String path = file.getAbsolutePath(); 
                if (path != null){
                    b = BitmapFactory.decodeFile(path);
                    imageView.setImageBitmap(b);
                }  
            } else { 
                new GetImages(imgurl, imageView, imagename).execute() ; 
            }

참고 :이 예제는 일반적으로 작동 할 수 있지만 오류 처리를 제공하지 않으며 AsyncTask의 장점에 대한 기본적인 이해도 부족합니다 (매개 변수화의 적절한 사용, 병렬 실행 ..). 자세한 내용은 아래의 예를 참조하십시오. 추신 . 어떤 이유로 든 내 자신의 코드를 홍보하기 위해 이것을 작성했다고 생각하는 사람들을 위해 : 아니요, 주어진 예제에서 볼 수있는 문제를 지적하고 있습니다.
Droidman

예 Droidman, 동의합니다. 이 코드는 탬 플레이트로 받아 들여 져야하며 오류 처리를 포함하여 스스로 완료해야합니다. 그런데 코드에도 오류 처리가 없습니다. IOException의 경우 연결 및 스트림은 어떻게됩니까?
Androider

@Androider 내 download방법, 특히 doInBackground내가 사용하는 작업 방법을 자세히 살펴보십시오 . 는 IOException에 착륙 할 catch (Throwable e)이 결과, 블록 ImageError반환하고, onError()콜백이 트리거된다. ImageError객체는 원래 스택 추적과 발생의 원인이 포함됩니다Exception
Droidman

1
예,하지만 연결이 끊어지지 않고 스트림이 닫히지 않습니다
Androider

@Androider 아, 나는 당신의 요지를 봅니다. 테스트 장치에서 의심스러운 누출을 발견하지 못했지만이 동작은 다른 장치에서 다를 수 있습니다. 덕분에 힌트를 위해 - 나는 코드를 업데이트 한
Droidman

33

왜 그것을 다운로드하기 위해 자신의 코드가 필요합니까? URI를 다운로드 관리자에 전달하는 것은 어떻습니까?

public void downloadFile(String uRl) {
    File direct = new File(Environment.getExternalStorageDirectory()
            + "/AnhsirkDasarp");

    if (!direct.exists()) {
        direct.mkdirs();
    }

    DownloadManager mgr = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);

    Uri downloadUri = Uri.parse(uRl);
    DownloadManager.Request request = new DownloadManager.Request(
            downloadUri);

    request.setAllowedNetworkTypes(
            DownloadManager.Request.NETWORK_WIFI
                    | DownloadManager.Request.NETWORK_MOBILE)
            .setAllowedOverRoaming(false).setTitle("Demo")
            .setDescription("Something useful. No, really.")
            .setDestinationInExternalPublicDir("/AnhsirkDasarp", "fileName.jpg");

    mgr.enqueue(request);

}

1
api> 8의 완벽한 예입니다.
Jeeten Parmar 2014 년

코드는 하나의 이미지에 완벽하게 작동합니다! 내 서버의 이미지 파일에서 더 많은 (100 개의 사진)을 다운로드하려면 어떻게해야합니까 ???
Kostantinos Ibra

파일이 손상되었다는 메시지가 표시됩니다!
Anup

2
아마도, .setDestinationInExternalPublicDir ( "/ AnhsirkDasarp", "fileName.jpg");
전문가는

2
어떻게 우리는 성공적으로 다운로드 또는 실패 여부를 알 수 있습니까
Prabs

12

도움이 될지도 ..

Button download_image = (Button)bigimagedialog.findViewById(R.id.btn_downloadimage);
                    download_image.setOnClickListener(new View.OnClickListener()
                    {
                        public void onClick(View v)
                        {
                            boolean success = (new File("/sdcard/dirname")).mkdir(); 
                            if (!success)
                            {
                                Log.w("directory not created", "directory not created");
                            }

                            try
                            {
                                URL url = new URL("YOUR_URL");
                                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                                connection.setDoInput(true);
                                connection.connect();
                                InputStream input = connection.getInputStream();
                                Bitmap myBitmap = BitmapFactory.decodeStream(input);

                                String data1 = String.valueOf(String.format("/sdcard/dirname/%d.jpg",System.currentTimeMillis()));

                                FileOutputStream stream = new FileOutputStream(data1);

                                ByteArrayOutputStream outstream = new ByteArrayOutputStream();
                                myBitmap.compress(Bitmap.CompressFormat.JPEG, 85, outstream);
                                byte[] byteArray = outstream.toByteArray();

                                stream.write(byteArray);
                                stream.close();

                                Toast.makeText(getApplicationContext(), "Downloading Completed", Toast.LENGTH_SHORT).show();
                            }
                            catch (Exception e)
                            {
                                e.printStackTrace();
                            }
                        }
                    });

6

완벽하게 작동하는 간단한 솔루션이 있습니다. 코드는 내 것이 아닙니다 . 이 링크 에서 찾았습니다 . 따라야 할 단계는 다음과 같습니다.

1. 이미지를 다운로드하기 전에 안드로이드 내부 저장소에 이미지 파일로 비트 맵을 저장하는 방법을 작성해 보겠습니다. 컨텍스트가 필요합니다. getApplicationContext ()에 의해 애플리케이션 컨텍스트에서 패스를 사용하는 것이 좋습니다. 이 메서드는 Activity 클래스 또는 다른 util 클래스에 덤프 될 수 있습니다.

public void saveImage(Context context, Bitmap b, String imageName) 
{
    FileOutputStream foStream;
    try 
    {
        foStream = context.openFileOutput(imageName, Context.MODE_PRIVATE);
        b.compress(Bitmap.CompressFormat.PNG, 100, foStream);
        foStream.close();
    } 
    catch (Exception e) 
    {
        Log.d("saveImage", "Exception 2, Something went wrong!");
        e.printStackTrace();
    }
}

2. 이제 andorid의 이미지 파일에 비트 맵을 저장하는 방법이 생겼습니다. URL로 이미지를 다운로드하는 AsyncTask를 작성해 보겠습니다. 이 개인 클래스는 활동 클래스에 하위 클래스로 배치되어야합니다. 이미지를 다운로드 한 후 onPostExecute 메서드에서 위에서 정의한 saveImage 메서드를 호출하여 이미지를 저장합니다. 이미지 이름은 "my_image.png"로 하드 코딩됩니다.

private class DownloadImage extends AsyncTask<String, Void, Bitmap> {
    private String TAG = "DownloadImage";
    private Bitmap downloadImageBitmap(String sUrl) {
        Bitmap bitmap = null;
        try {
            InputStream inputStream = new URL(sUrl).openStream();   // Download Image from URL
            bitmap = BitmapFactory.decodeStream(inputStream);       // Decode Bitmap
            inputStream.close();
        } catch (Exception e) {
            Log.d(TAG, "Exception 1, Something went wrong!");
            e.printStackTrace();
        }
        return bitmap;
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        return downloadImageBitmap(params[0]);
    }

    protected void onPostExecute(Bitmap result) {
        saveImage(getApplicationContext(), result, "my_image.png");
    }
}

3. 이미지를 다운로드하기위한 AsyncTask가 정의되어 있지만 해당 AsyncTask를 실행하려면 실행해야합니다. 이렇게하려면 Activity 클래스의 onCreate 메서드 또는 버튼의 onClick 메서드 또는 적합하다고 생각되는 다른 위치에이 줄을 작성합니다.

new DownloadImage().execute("http://developer.android.com/images/activity_lifecycle.png");

이미지는 /data/data/your.app.packagename/files/my_image.jpeg에 저장되어야합니다. 장치에서이 디렉토리에 액세스하려면이 게시물을 확인하십시오.

IMO 이것은 문제를 해결합니다! 이미지로드와 같은 추가 단계를 원하는 경우 다음 추가 단계를 수행 할 수 있습니다.

4. 이미지를 다운로드 한 후 내부 저장소에서 이미지 비트 맵을로드하여 사용할 수있는 방법이 필요합니다. 이미지 비트 맵을로드하는 방법을 작성해 보겠습니다. 이 메서드는 전체 경로없이 컨텍스트와 이미지 파일 이름이라는 두 개의 매개 변수를 사용합니다. context.openFileInput (imageName)은이 파일 이름이 위의 saveImage 메서드에 저장되었을 때 저장 디렉토리에서 파일을 조회합니다.

public Bitmap loadImageBitmap(Context context, String imageName) {
    Bitmap bitmap = null;
    FileInputStream fiStream;
    try {
        fiStream    = context.openFileInput(imageName);
        bitmap      = BitmapFactory.decodeStream(fiStream);
        fiStream.close();
    } catch (Exception e) {
        Log.d("saveImage", "Exception 3, Something went wrong!");
        e.printStackTrace();
    }
    return bitmap;
}

5. 이제 ImageView 또는 이미지를 사용하려는 다른 뷰의 이미지를 설정하는 데 필요한 모든 것이 있습니다. 이미지를 저장할 때 이미지 이름을 "my_image.jpeg"로 하드 코딩했습니다. 이제이 이미지 이름을 위의 loadImageBitmap 메서드에 전달하여 비트 맵을 가져와 ImageView로 설정할 수 있습니다.

someImageView.setImageBitmap(loadImageBitmap(getApplicationContext(), "my_image.jpeg"));

6. 이미지 이름으로 이미지 전체 경로를 얻으려면.

File file            = getApplicationContext().getFileStreamPath("my_image.jpeg");
String imageFullPath = file.getAbsolutePath();

7. 이미지 파일이 있는지 확인합니다.

파일 파일 =

getApplicationContext().getFileStreamPath("my_image.jpeg");
if (file.exists()) Log.d("file", "my_image.jpeg exists!");
  1. 이미지 파일을 삭제합니다.

    파일 파일 = getApplicationContext (). getFileStreamPath ( "my_image.jpeg"); if (file.delete ()) Log.d ( "file", "my_image.jpeg 삭제됨!");


0

이 코드는 내 프로젝트에서 완벽하게 실행됩니다.

downloadImagesToSdCard(imagepath,imagepath);

private void downloadImagesToSdCard(String downloadUrl,String imageName)
        {
            try
            {
                URL url = new URL("www.xxx.com"+downloadUrl); 
                /* making a directory in sdcard */
            //  String sdCard=Environment.getExternalStorageDirectory().toString();
                ContextWrapper cw = new ContextWrapper(getActivity());
                 // path to /data/data/yourapp/app_data/imageDir
                File directory = cw.getDir("files", Context.MODE_PRIVATE);

                File myDir = new File(directory,"folder");

                /*  if specified not exist create new */
                if(!myDir.exists())
                {
                    myDir.mkdir();
                    Log.v("", "inside mkdir");
                }

                /* checks the file and if it already exist delete */
                String fname = imageName;
                File file = new File (myDir, fname);
                Log.d("file===========path", ""+file);
                if (file.exists ()) 
                    file.delete (); 

                /* Open a connection */
                URLConnection ucon = url.openConnection();
                InputStream inputStream = null;
                HttpURLConnection httpConn = (HttpURLConnection)ucon;
                httpConn.setRequestMethod("GET");
                httpConn.connect();
                inputStream = httpConn.getInputStream();
                /*if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) 
                {
                    inputStream = httpConn.getInputStream();
                }*/

                FileOutputStream fos = new FileOutputStream(file);  
                int totalSize = httpConn.getContentLength();
                int downloadedSize = 0;   
                byte[] buffer = new byte[1024];
                int bufferLength = 0;
                while ( (bufferLength = inputStream.read(buffer)) >0 ) 
                {                 
                    fos.write(buffer, 0, bufferLength);                  
                    downloadedSize += bufferLength;                 
                    Log.i("Progress:","downloadedSize:"+downloadedSize+"totalSize:"+ totalSize) ;
                }   

                fos.close();
                Log.d("test", "Image Saved in sdcard..");  
                viewimage();
            }
            catch(IOException io)
            {                  
                io.printStackTrace();
            }
            catch(Exception e)
            {                     
                e.printStackTrace();
            }


        } 

        public void viewimage()
        {
              String path = serialnumber+".png";
               ContextWrapper cw = new ContextWrapper(getActivity());

                //path to /data/data/yourapp/app_data/dirName
                File directory = cw.getDir("files", Context.MODE_PRIVATE);

                File mypath=new File(directory,"folder/"+path);

                Bitmap b;
                try {
                    b = BitmapFactory.decodeStream(new FileInputStream(mypath));
                //  b.compress(format, quality, stream)
                    profile_image.setImageBitmap(Bitmap.createScaledBitmap(b, 120, 120, false));
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
        }

1
안녕하세요, 기여해 주셔서 감사합니다. 앱의 개인 저장소에 이미지를 저장하는 위의 접근 방식에는 2 가지 단점이 있습니다. 1) 이미지는 앱에서만 사용할 수 있으며 2) 이미지는 대용량 콘텐츠 용이 아니므로 앱의 개인 저장소에 저장하지 않아야합니다.
Droidman

0

이 시도

 try
                {
                    Bitmap bmp = null;
                    URL url = new URL("Your_URL");
                    URLConnection conn = url.openConnection();
                    bmp = BitmapFactory.decodeStream(conn.getInputStream());
                    File f = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis() + ".jpg");
                    if(f.exists())
                        f.delete();
                    f.createNewFile();
                    Bitmap bitmap = bmp;
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
                    byte[] bitmapdata = bos.toByteArray();
                    FileOutputStream fos = new FileOutputStream(f);
                    fos.write(bitmapdata);
                    fos.flush();
                    fos.close();
                    Log.e(TAG, "imagepath: "+f );
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }

0
public class testCrop extends AppCompatActivity {
    ImageView iv;
    String imagePath = "https://style.pk/wp-content/uploads/2015/07/omer-Shahzad-performed-umrah-600x548.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.testcrpop);
        iv = (ImageView) findViewById(R.id.testCrop);
        imageDownload image = new imageDownload(testCrop.this, iv);
        image.execute(imagePath);
    }
    class imageDownload extends AsyncTask<String, Integer, Bitmap> {
        Context context;
        ImageView imageView;
        Bitmap bitmap;
        InputStream in = null;
        int responseCode = -1;
//constructor.
        public imageDownload(Context context, ImageView imageView) {
            this.context = context;
            this.imageView = imageView;
        }
        @Override
        protected void onPreExecute() {
        }
        @Override
        protected Bitmap doInBackground(String... params) {
            try {
                URL url = new URL(params[0]);
                HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                httpURLConnection.setDoOutput(true);
                httpURLConnection.connect();
                responseCode = httpURLConnection.getResponseCode();
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    in = httpURLConnection.getInputStream();
                    bitmap = BitmapFactory.decodeStream(in);
                    in.close();
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bitmap;
        }
        @Override
        protected void onPostExecute(Bitmap data) {
            imageView.setImageBitmap(data);
            saveImage(data);
        }

        private void saveImage(Bitmap data) {
            File createFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"test");
            createFolder.mkdir();
            File saveImage = new File(createFolder,"downloadimage.jpg");
            try {
                OutputStream outputStream = new FileOutputStream(saveImage);
                data.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
                outputStream.flush();
                outputStream.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

산출여기에 이미지 설명 입력

메모리에 데이터 쓰기 권한을 추가했는지 확인하십시오.

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

0

@Droidman 게시물은 꽤 포괄적입니다. Volley는 몇 KB의 작은 데이터로 잘 작동합니다. 'BasicImageDownloader.java'를 사용하려고 할 때 Android Studio는 AsyncTask 클래스가 정적이어야하거나 누수가있을 수 있다고 경고했습니다. 다른 테스트 앱에서 Volley를 사용했는데 누출로 인해 계속 충돌이 발생했기 때문에 이미지 다운로더에 Volley를 사용하는 것이 걱정됩니다 (이미지는 100kB가 될 수 있음).

나는 Picasso를 사용했고 그것은 잘 작동했고, 위에 게시 된 것에서 약간의 변화 (아마 Picasso에 대한 업데이트)가 있습니다. 아래 코드는 나를 위해 일했습니다.

    public static void imageDownload(Context ctx, String url){
    Picasso.get().load(yourURL)
            .into(getTarget(url));
}
private static Target getTarget(final String url){

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

                @Override
                public void run() {

                    File file = new File(localPath + "/"+"YourImageFile.jpg");
                    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(Exception e, Drawable errorDrawable) {
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
        }
    };
    return target;
}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.