android - lưu hình ảnh vào thư viện


91

tôi có một ứng dụng có thư viện hình ảnh và tôi muốn người dùng có thể lưu nó vào thư viện của riêng mình. Tôi đã tạo một menu tùy chọn với một giọng nói duy nhất "lưu" để cho phép điều đó nhưng vấn đề là ... làm thế nào tôi có thể lưu hình ảnh vào thư viện?

đây là mã của tôi:

@Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle item selection
            switch (item.getItemId()) {
            case R.id.menuFinale:

                imgView.setDrawingCacheEnabled(true);
                Bitmap bitmap = imgView.getDrawingCache();
                File root = Environment.getExternalStorageDirectory();
                File file = new File(root.getAbsolutePath()+"/DCIM/Camera/img.jpg");
                try 
                {
                    file.createNewFile();
                    FileOutputStream ostream = new FileOutputStream(file);
                    bitmap.compress(CompressFormat.JPEG, 100, ostream);
                    ostream.close();
                } 
                catch (Exception e) 
                {
                    e.printStackTrace();
                }



                return true;
            default:
                return super.onOptionsItemSelected(item);
            }
        }

tôi không chắc về phần mã này:

File root = Environment.getExternalStorageDirectory();
                File file = new File(root.getAbsolutePath()+"/DCIM/Camera/img.jpg");

là nó chính xác để lưu vào thư viện? tiếc là mã không hoạt động :(


bạn đã giải quyết vấn đề này chưa? bạn có thể vui lòng chia sẻ với tôi không
user3233280 22/02/14

tôi cũng đang gặp vấn đề tương tự stackoverflow.com/questions/21951558/…
user3233280

Đối với những người vẫn gặp sự cố khi lưu tệp, có thể do url của bạn chứa các ký tự không hợp lệ như "?", ":", Và "-" Hãy xóa chúng đi và nó sẽ hoạt động. Đây là một lỗi phổ biến ở các thiết bị nước ngoài và các trình giả lập Android. Xem thêm về nó tại đây: stackoverflow.com/questions/11394616/…
ChallengeAccepted vào

Câu trả lời được chấp nhận là một chút lỗi thời vào năm 2019. Tôi đã viết một câu trả lời cập nhật tại đây: stackoverflow.com/questions/36624756/...
Bảo Lei

Câu trả lời:


168
MediaStore.Images.Media.insertImage(getContentResolver(), yourBitmap, yourTitle , yourDescription);

Mã cũ sẽ thêm hình ảnh vào cuối thư viện. Nếu bạn muốn sửa đổi ngày để ngày xuất hiện ở đầu hoặc bất kỳ siêu dữ liệu nào khác, hãy xem đoạn mã dưới đây (Được sự hỗ trợ của SK, samkirton ):

https://gist.github.com/samkirton/0242ba81d7ca00b475b9

/**
 * Android internals have been modified to store images in the media folder with 
 * the correct date meta data
 * @author samuelkirton
 */
public class CapturePhotoUtils {

    /**
     * A copy of the Android internals  insertImage method, this method populates the 
     * meta data with DATE_ADDED and DATE_TAKEN. This fixes a common problem where media 
     * that is inserted manually gets saved at the end of the gallery (because date is not populated).
     * @see android.provider.MediaStore.Images.Media#insertImage(ContentResolver, Bitmap, String, String)
     */
    public static final String insertImage(ContentResolver cr, 
            Bitmap source, 
            String title, 
            String description) {

        ContentValues values = new ContentValues();
        values.put(Images.Media.TITLE, title);
        values.put(Images.Media.DISPLAY_NAME, title);
        values.put(Images.Media.DESCRIPTION, description);
        values.put(Images.Media.MIME_TYPE, "image/jpeg");
        // Add the date meta data to ensure the image is added at the front of the gallery
        values.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
        values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());

        Uri url = null;
        String stringUrl = null;    /* value to be returned */

        try {
            url = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

            if (source != null) {
                OutputStream imageOut = cr.openOutputStream(url);
                try {
                    source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
                } finally {
                    imageOut.close();
                }

                long id = ContentUris.parseId(url);
                // Wait until MINI_KIND thumbnail is generated.
                Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);
                // This is for backward compatibility.
                storeThumbnail(cr, miniThumb, id, 50F, 50F,Images.Thumbnails.MICRO_KIND);
            } else {
                cr.delete(url, null, null);
                url = null;
            }
        } catch (Exception e) {
            if (url != null) {
                cr.delete(url, null, null);
                url = null;
            }
        }

        if (url != null) {
            stringUrl = url.toString();
        }

        return stringUrl;
    }

    /**
     * A copy of the Android internals StoreThumbnail method, it used with the insertImage to
     * populate the android.provider.MediaStore.Images.Media#insertImage with all the correct
     * meta data. The StoreThumbnail method is private so it must be duplicated here.
     * @see android.provider.MediaStore.Images.Media (StoreThumbnail private method)
     */
    private static final Bitmap storeThumbnail(
            ContentResolver cr,
            Bitmap source,
            long id,
            float width, 
            float height,
            int kind) {

        // create the matrix to scale it
        Matrix matrix = new Matrix();

        float scaleX = width / source.getWidth();
        float scaleY = height / source.getHeight();

        matrix.setScale(scaleX, scaleY);

        Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
            source.getWidth(),
            source.getHeight(), matrix,
            true
        );

        ContentValues values = new ContentValues(4);
        values.put(Images.Thumbnails.KIND,kind);
        values.put(Images.Thumbnails.IMAGE_ID,(int)id);
        values.put(Images.Thumbnails.HEIGHT,thumb.getHeight());
        values.put(Images.Thumbnails.WIDTH,thumb.getWidth());

        Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);

        try {
            OutputStream thumbOut = cr.openOutputStream(url);
            thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
            thumbOut.close();
            return thumb;
        } catch (FileNotFoundException ex) {
            return null;
        } catch (IOException ex) {
            return null;
        }
    }
}

22
Thao tác này sẽ lưu hình ảnh, nhưng ở cuối thư viện, mặc dù khi bạn chụp ảnh bằng máy ảnh, nó sẽ lưu ở trên cùng. Làm cách nào để lưu hình ảnh vào đầu thư viện?
eric.itzhak

19
Lưu ý rằng bạn cũng phải thêm <use-allow android: name = "android.permission.WRITE_EXTERNAL_STORAGE" /> vào manifext.xml của mình.
Kyle Clegg

3
Hình ảnh không được lưu ở đầu thư viện vì insertImage nội bộ không thêm bất kỳ dữ liệu meta ngày tháng nào. Vui lòng xem GIST này: gist.github.com/0242ba81d7ca00b475b9.git nó là một bản sao chính xác của phương thức insertImage nhưng nó thêm meta date để đảm bảo hình ảnh được thêm vào phía trước của thư viện.
S-K

1
@ S-K'Tôi không thể truy cập URL đó. Vui lòng cập nhật nó và tôi sẽ cập nhật câu trả lời của mình để nó có cả hai lựa chọn. Chúc mừng
sfratini

6
Dưới đây là liên kết đúng GIST nêu trên (cần thiết để loại bỏ các .gitở cuối)
minipif

48

Trên thực tế, bạn có thể lưu hình ảnh của bạn ở bất kỳ nơi nào. Nếu bạn muốn lưu trong không gian công cộng để bất kỳ ứng dụng nào khác có thể truy cập, hãy sử dụng mã này:

storageDir = new File(
    Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    ), 
    getAlbumName()
);

Hình ảnh không có trong album. Để làm điều này, bạn cần gọi quét:

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

Bạn có thể tìm thêm thông tin tại https://developer.android.com/training/camera/photobasics.html#TaskGallery


1
Đây là một giải pháp đơn giản tuyệt vời vì chúng ta không cần phải thay đổi toàn bộ cách triển khai và chúng ta có thể tạo một thư mục tùy chỉnh cho các ứng dụng.
Hugo Gresse

2
Việc gửi một chương trình phát sóng có thể lãng phí tài nguyên khi bạn chỉ có thể quét một tệp: stackoverflow.com/a/5814533/43051 .
Jérémy Reynaud

2
Bạn thực sự vượt qua bitmap ở đâu?
Daniel Reyhanian

22

Tôi đã thử rất nhiều thứ để cho phép điều này hoạt động trên Marshmallow và Lollipop. Cuối cùng, tôi đã kết thúc việc chuyển ảnh đã lưu vào thư mục DCIM (ứng dụng Google Photo mới chỉ quét ảnh nếu chúng có bên trong thư mục này)

public static File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
         .format(System.currentTimeInMillis());
    File storageDir = new File(Environment
         .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/");
    if (!storageDir.exists())
        storageDir.mkdirs();
    File image = File.createTempFile(
            timeStamp,                   /* prefix */
            ".jpeg",                     /* suffix */
            storageDir                   /* directory */
    );
    return image;
}

Và sau đó là mã chuẩn để quét tệp mà bạn cũng có thể tìm thấy trong trang web Nhà phát triển của Google .

public static void addPicToGallery(Context context, String photoPath) {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(photoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    context.sendBroadcast(mediaScanIntent);
}

Hãy nhớ rằng thư mục này không thể có trong mọi thiết bị trên thế giới và bắt đầu từ Marshmallow (API 23), bạn cần yêu cầu người dùng cấp quyền WRITE_EXTERNAL_STORAGE.


1
Cảm ơn vì thông tin liên quan đến Google Photos.
Jérémy Reynaud

1
Đây là một giải pháp duy nhất giải thích tốt. Không ai khác đề cập rằng tệp phải nằm trong thư mục DCIM. Cảm ơn bạn!!!
Predrag Manojlovic

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)đã làm thủ thuật cho tôi. Cảm ơn!
saltandpepper

2
getExternalStoragePublicDirectory()hiện không được dùng nữa trên API 29. Cần sử dụng MediaStore
riggaroo

@riggaroo Có bạn là đúng Rebecca, tôi sẽ cập nhật câu trả lời càng sớm càng tốt
MatPag

13

Theo khóa học này , cách chính xác để làm điều này là:

Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    )

Điều này sẽ cung cấp cho bạn đường dẫn gốc cho thư mục thư viện.


tôi đã cố gắng mã mới này, nhưng nó đã bị rơi java.lang.NoSuchFieldError: android.os.Environment.DIRECTORY_PICTURES
Christian Giupponi

ok cảm ơn, vậy không có cách nào để đưa hình ảnh vào thư viện với android <2.2?
Christian Giupponi

Hoàn hảo - một liên kết trực tiếp đến trang web Nhà phát triển Android. Điều này đã hiệu quả và là một giải pháp đơn giản.
Phil

1
Câu trả lời hay, nhưng sẽ tốt hơn nếu bổ sung phương thức "galleryAddPic" từ các câu trả lời khác ở đây, vì bạn thường sẽ muốn ứng dụng Thư viện nhận thấy ảnh mới.
Andrew Koster

11
private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

6

Bạn có thể tạo một thư mục bên trong thư mục máy ảnh và lưu hình ảnh. Sau đó, bạn có thể chỉ cần thực hiện quét của mình. Nó sẽ ngay lập tức hiển thị hình ảnh của bạn trong thư viện.

String root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString()+ "/Camera/Your_Directory_Name";
File myDir = new File(root);
myDir.mkdirs();
String fname = "Image-" + image_name + ".png";
File file = new File(myDir, fname);
System.out.println(file.getAbsolutePath());
if (file.exists()) file.delete();
    Log.i("LOAD", root + fname);
    try {
        FileOutputStream out = new FileOutputStream(file);
        finalBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
        out.flush();
        out.close();
    } catch (Exception e) {
       e.printStackTrace();
    }

MediaScannerConnection.scanFile(context, new String[]{file.getPath()}, new String[]{"image/jpeg"}, null);

trong tiêu chí này, đây là câu trả lời tốt nhất
Noor Hossain

1

Tôi đến đây với cùng một nghi ngờ nhưng đối với Xamarin dành cho Android, tôi đã sử dụng câu trả lời Sigrist để thực hiện phương pháp này sau khi lưu tệp của mình:

private void UpdateGallery()
{
    Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
    Java.IO.File file = new Java.IO.File(_path);
    Android.Net.Uri contentUri = Android.Net.Uri.FromFile(file);
    mediaScanIntent.SetData(contentUri);
    Application.Context.SendBroadcast(mediaScanIntent);
} 

và nó đã giải quyết được vấn đề của tôi, Thx Sigrist. Tôi đặt nó ở đây vì tôi không tìm thấy câu trả lời về điều này cho Xamarin và tôi hy vọng nó có thể giúp ích cho những người khác.


1

Trong trường hợp của tôi, các giải pháp trên không hoạt động, tôi phải làm như sau:

sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(f)));

của nó thực sự tốt để biết về tùy chọn này, nhưng tiếc là không hoạt động trên một số thiết bị với android 6, vì vậy ContentProvidersolytion thích hợp hơn
Siarhei

0
 String filePath="/storage/emulated/0/DCIM"+app_name;
    File dir=new File(filePath);
    if(!dir.exists()){
        dir.mkdir();
    }

Mã này nằm trong phương thức onCreate, mã này dùng để tạo thư mục app_name. Bây giờ, thư mục này có thể được truy cập bằng ứng dụng quản lý tệp mặc định trong Android. Sử dụng chuỗi filePath này bất cứ nơi nào cần thiết để đặt thư mục đích của bạn. Tôi chắc chắn rằng phương pháp này cũng hoạt động trên Android 7 vì tôi đã thử nghiệm trên nó. Do đó, nó cũng có thể hoạt động trên các phiên bản Android khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.