Android将文件保存到外部存储

Posted

技术标签:

【中文标题】Android将文件保存到外部存储【英文标题】:Android saving file to external storage 【发布时间】:2011-12-14 18:48:26 【问题描述】:

我在我的 android 应用程序上创建目录并将文件保存到其中时遇到了一点问题。我正在使用这段代码来做到这一点:

String filename = "MyApp/MediaTag/MediaTag-"+objectId+".png";
File file = new File(Environment.getExternalStorageDirectory(), filename);
FileOutputStream fos;

fos = new FileOutputStream(file);
fos.write(mediaTagBuffer);
fos.flush();
fos.close();

但它抛出了一个异常:

java.io.FileNotFoundException: /mnt/sdcard/MyApp/MediaCard/MediaCard-0.png(没有这样的文件或目录)

在那条线上:fos = new FileOutputStream(file);

如果我将文件名设置为:"MyApp/MediaTag-"+objectId+" 它可以工作,但是如果我尝试创建文件并将其保存到另一个目录,则会引发异常。那么有什么想法我做错了吗?

还有一个问题:有什么方法可以将我的文件在外部存储中设为私有,这样用户就无法在图库中看到它们,除非他将他的设备连接为Disk Drive

【问题讨论】:

【参考方案1】:

使用此功能将您的位图保存在 SD 卡中

private void SaveImage(Bitmap finalBitmap) 

    String root = Environment.getExternalStorageDirectory().toString();
    File myDir = new File(root + "/saved_images");    
     if (!myDir.exists()) 
                    myDir.mkdirs();
                
    Random generator = new Random();
    int n = 10000;
    n = generator.nextInt(n);
    String fname = "Image-"+ n +".jpg";
    File file = new File (myDir, fname);
    if (file.exists ())
      file.delete (); 
    try 
        FileOutputStream out = new FileOutputStream(file);
        finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
        out.flush();
        out.close();

     catch (Exception e) 
         e.printStackTrace();
    

并将其添加到清单中

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

编辑:通过使用此行,您将能够在图库视图中看到保存的图像。

sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
                         Uri.parse("file://" + Environment.getExternalStorageDirectory())));

看看这个链接也http://rajareddypolam.wordpress.com/?p=3&preview=true

【讨论】:

你应该仍然使用Environment.getExternalStorageDirectory()而不是/sdcard 它只保存在您的文件夹中,它显示在相机中意味着您正在通过相机自动拍摄图像它存储在相机中.. 请使用finally,不要抓通用Exception @LiamGeorgeBetsworth 所有上述行为原样前KitKat中起作用。 不适合使用Intent.ACTION_MEDIA_MOUNTED,并且不适用于KitKat。正确的广播意图是new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file) )【参考方案2】:

RajaReddy 提供的代码不再适用于 KitKat

这个做了(2 处改动):

private void saveImageToExternalStorage(Bitmap finalBitmap) 
    String root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
    File myDir = new File(root + "/saved_images");
    myDir.mkdirs();
    Random generator = new Random();
    int n = 10000;
    n = generator.nextInt(n);
    String fname = "Image-" + n + ".jpg";
    File file = new File(myDir, fname);
    if (file.exists())
        file.delete();
    try 
        FileOutputStream out = new FileOutputStream(file);
        finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
        out.flush();
        out.close();
    
    catch (Exception e) 
        e.printStackTrace();
    


    // Tell the media scanner about the new file so that it is
    // immediately available to the user.
    MediaScannerConnection.scanFile(this, new String[]  file.toString() , null,
            new MediaScannerConnection.OnScanCompletedListener() 
                public void onScanCompleted(String path, Uri uri) 
                    Log.i("ExternalStorage", "Scanned " + path + ":");
                    Log.i("ExternalStorage", "-> uri=" + uri);
                
    );


【讨论】:

我的 uri 为空? 将新文件告知媒体扫描仪,以便用户立即使用它 - 它可以节省我的时间【参考方案3】:

2018 年更新,SDK >= 23。

现在您还应该使用以下方法检查用户是否已授予外部存储权限:

public boolean isStoragePermissionGranted() 
    String TAG = "Storage Permission";
    if (Build.VERSION.SDK_INT >= 23) 
        if (this.checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED) 
            Log.v(TAG, "Permission is granted");
            return true;
         else 
            Log.v(TAG, "Permission is revoked");
            ActivityCompat.requestPermissions(this, new String[]Manifest.permission.WRITE_EXTERNAL_STORAGE, 1);
            return false;
        
    
    else  //permission is automatically granted on sdk<23 upon installation
        Log.v(TAG,"Permission is granted");
        return true;
    


public void saveImageBitmap(Bitmap image_bitmap, String image_name) 
    String root = Environment.getExternalStorageDirectory().toString();
    if (isStoragePermissionGranted())  // check or ask permission
        File myDir = new File(root, "/saved_images");
        if (!myDir.exists()) 
            myDir.mkdirs();
        
        String fname = "Image-" + image_name + ".jpg";
        File file = new File(myDir, fname);
        if (file.exists()) 
            file.delete();
        
        try 
            file.createNewFile(); // if file already exists will do nothing
            FileOutputStream out = new FileOutputStream(file);
            image_bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
            out.flush();
            out.close();

         catch (Exception e) 
            e.printStackTrace();
        

        MediaScannerConnection.scanFile(this, new String[]file.toString(), new String[]file.getName(), null);
    

当然,添加AndroidManifest.xml:

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

【讨论】:

【参考方案4】:

你需要这个权限

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

和方法:

public boolean saveImageOnExternalData(String filePath, byte[] fileData) 

    boolean isFileSaved = false;
    try 
        File f = new File(filePath);
        if (f.exists())
            f.delete();
        f.createNewFile();
        FileOutputStream fos = new FileOutputStream(f);
        fos.write(fileData);
        fos.flush();
        fos.close();
        isFileSaved = true;
        // File Saved
     catch (FileNotFoundException e) 
        System.out.println("FileNotFoundException");
        e.printStackTrace();
     catch (IOException e) 
        System.out.println("IOException");
        e.printStackTrace();
    
    return isFileSaved;
    // File Not Saved

【讨论】:

【参考方案5】:

确保您的应用具有适当的权限以允许写入外部存储:http://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE

在清单文件中应该如下所示:

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

【讨论】:

【参考方案6】:

试试这个:

    检查外部存储设备 写入文件 读取文件
public class WriteSDCard extends Activity 

    private static final String TAG = "MEDIA";
    private TextView tv;

    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        tv = (TextView) findViewById(R.id.TextView01);
        checkExternalMedia();
        writeToSDFile();
        readRaw();
    

    /**
     * Method to check whether external media available and writable. This is
     * adapted from
     * http://developer.android.com/guide/topics/data/data-storage.html
     * #filesExternal
     */
    private void checkExternalMedia() 
        boolean mExternalStorageAvailable = false;
        boolean mExternalStorageWriteable = false;
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) 
            // Can read and write the media
            mExternalStorageAvailable = mExternalStorageWriteable = true;
         else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) 
            // Can only read the media
            mExternalStorageAvailable = true;
            mExternalStorageWriteable = false;
         else 
            // Can't read or write
            mExternalStorageAvailable = mExternalStorageWriteable = false;
        
        tv.append("\n\nExternal Media: readable=" + mExternalStorageAvailable
            + " writable=" + mExternalStorageWriteable);
    

    /**
     * Method to write ascii text characters to file on SD card. Note that you
     * must add a WRITE_EXTERNAL_STORAGE permission to the manifest file or this
     * method will throw a FileNotFound Exception because you won't have write
     * permission.
     */
    private void writeToSDFile() 
        // Find the root of the external storage.
        // See http://developer.android.com/guide/topics/data/data-
        // storage.html#filesExternal
        File root = android.os.Environment.getExternalStorageDirectory();
        tv.append("\nExternal file system root: " + root);
        // See
        // http://***.com/questions/3551821/android-write-to-sd-card-folder
        File dir = new File(root.getAbsolutePath() + "/download");
        dir.mkdirs();
        File file = new File(dir, "myData.txt");
        try 
            FileOutputStream f = new FileOutputStream(file);
            PrintWriter pw = new PrintWriter(f);
            pw.println("Hi , How are you");
            pw.println("Hello");
            pw.flush();
            pw.close();
            f.close();
         catch (FileNotFoundException e) 
            e.printStackTrace();
            Log.i(TAG, "******* File not found. Did you"
                + " add a WRITE_EXTERNAL_STORAGE permission to the   manifest?");
         catch (IOException e) 
            e.printStackTrace();
        
        tv.append("\n\nFile written to " + file);
    

    /**
     * Method to read in a text file placed in the res/raw directory of the
     * application. The method reads in all lines of the file sequentially.
     */
    private void readRaw() 
        tv.append("\nData read from res/raw/textfile.txt:");
        InputStream is = this.getResources().openRawResource(R.raw.textfile);
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr, 8192); // 2nd arg is buffer
        // size
        // More efficient (less readable) implementation of above is the
        // composite expression
        /*
         * BufferedReader br = new BufferedReader(new InputStreamReader(
         * this.getResources().openRawResource(R.raw.textfile)), 8192);
         */
        try 
            String test;
            while (true) 
                test = br.readLine();
                // readLine() returns null if no more lines in the file
                if (test == null) break;
                tv.append("\n" + "    " + test);
            
            isr.close();
            is.close();
            br.close();
         catch (IOException e) 
            e.printStackTrace();
        
        tv.append("\n\nThat is all");
    

【讨论】:

这看起来与此处的代码非常相似:***.com/a/8330635/19679。如果它是从那里提取的,您可能应该在答案中引用它。【参考方案7】:

我创建了一个用于保存位图的 AsyncTask。

public class BitmapSaver extends AsyncTask<Void, Void, Void>

    public static final String TAG ="BitmapSaver";

    private Bitmap bmp;

    private Context ctx;

    private File pictureFile;

    public BitmapSaver(Context paramContext , Bitmap paramBitmap)
    
        ctx = paramContext;

        bmp = paramBitmap;
    

    /** Create a File for saving an image or video */
    private  File getOutputMediaFile()
    
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this. 
        File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
                + "/Android/data/"
                + ctx.getPackageName()
                + "/Files"); 

        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists())
            if (! mediaStorageDir.mkdirs())
                return null;
            
         
        // Create a media file name
        String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
        File mediaFile;
            String mImageName="MI_"+ timeStamp +".jpg";
            mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);  
        return mediaFile;

     
    protected Void doInBackground(Void... paramVarArgs)
       
        this.pictureFile = getOutputMediaFile();

        if (this.pictureFile == null)  return null; 

        try
        
            FileOutputStream localFileOutputStream = new FileOutputStream(this.pictureFile);
            this.bmp.compress(Bitmap.CompressFormat.PNG, 90, localFileOutputStream);
            localFileOutputStream.close();
        
        catch (FileNotFoundException localFileNotFoundException)
        
            return null;
        
        catch (IOException localIOException)
        
        
        return null;
    

    protected void onPostExecute(Void paramVoid)
    
        super.onPostExecute(paramVoid);

        try
        
            //it will help you broadcast and view the saved bitmap in Gallery
            this.ctx.sendBroadcast(new Intent("android.intent.action.MEDIA_MOUNTED", Uri
                    .parse("file://" + Environment.getExternalStorageDirectory())));

            Toast.makeText(this.ctx, "File saved", 0).show();

            return;
        
        catch (Exception localException1)
        
            try
            
                Context localContext = this.ctx;
                String[] arrayOfString = new String[1];
                arrayOfString[0] = this.pictureFile.toString();
                MediaScannerConnection.scanFile(localContext, arrayOfString, null,
                        new MediaScannerConnection.OnScanCompletedListener()
                        
                            public void onScanCompleted(String paramAnonymousString ,
                                    Uri paramAnonymousUri)
                            
                            
                        );
                return;
            
            catch (Exception localException2)
            
            
        
    

【讨论】:

如何保存 gif 图片?? Gif 图片包含多张图片。您必须先分离这些框架,然后才能使用此方法。这是我的意见。 我是从***.com/questions/39826400/…做的【参考方案8】:

可能会抛出异常,因为没有MediaCard 子目录。您应该检查路径中的所有目录是否存在。

关于文件的可见性:如果您将名为 .nomedia 的文件放在您的目录中,您就是在告诉 Android 您不希望它扫描它以查找媒体文件,并且它们不会出现在图库中。

【讨论】:

【参考方案9】:

自从 android 4.4 文件保存已更改。有

ContextCompat.getExternalFilesDirs(context, name);

它返回一个数组。

当名字为空时

第一个值类似于 /storage/emulated/0/Android/com.my.package/files

第二个值就像 /storage/extSdCard/Android/com.my.package/files

android 4.3 及以下版本会返回单个项目数组

部分杂乱无章的代码,但它演示了它是如何工作的:

    /** Create a File for saving an image or video 
     * @throws Exception */
    private File getOutputMediaFile(int type) throws Exception

        // Check that the SDCard is mounted
        File mediaStorageDir;
        if(internalstorage.isChecked())
        
            mediaStorageDir = new File(getFilesDir().getAbsolutePath() );
        
        else
        
            File[] dirs=ContextCompat.getExternalFilesDirs(this, null);
            mediaStorageDir = new File(dirs[dirs.length>1?1:0].getAbsolutePath() );
        


        // Create the storage directory(MyCameraVideo) if it does not exist
        if (! mediaStorageDir.exists())

            if (! mediaStorageDir.mkdirs())

                output.setText("Failed to create directory.");

                Toast.makeText(this, "Failed to create directory.", Toast.LENGTH_LONG).show();

                Log.d("myapp", "Failed to create directory");
                return null;
            
        


        // Create a media file name

        // For unique file name appending current timeStamp with file name
        java.util.Date date= new java.util.Date();
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",Locale.ENGLISH)  .format(date.getTime());

        File mediaFile;

        if(type == MEDIA_TYPE_VIDEO) 

            // For unique video file name appending current timeStamp with file name
            mediaFile = new File(mediaStorageDir.getPath() + File.separator + slpid + "_" + pwsid + "_" + timeStamp + ".mp4");

        
        else if(type == MEDIA_TYPE_AUDIO) 

            // For unique video file name appending current timeStamp with file name
            mediaFile = new File(mediaStorageDir.getPath() + File.separator + slpid + "_" + pwsid + "_" + timeStamp + ".3gp");

         else 
            return null;
        

        return mediaFile;
    



    /** Create a file Uri for saving an image or video 
     * @throws Exception */
    private  Uri getOutputMediaFileUri(int type) throws Exception

          return Uri.fromFile(getOutputMediaFile(type));
    

//usage:
        try 
            file=getOutputMediaFileUri(MEDIA_TYPE_AUDIO).getPath();
         catch (Exception e1) 
            e1.printStackTrace();
            return;
        

【讨论】:

【参考方案10】:

对于 API 级别 23 (Marshmallow) 及更高版本,除了 manifest 中的使用权限外,还应实现弹出权限,并且用户需要在运行时使用应用程序时授予它。

下面有一个例子,将hello world!保存为图片目录内Test目录下myFile.txt文件的内容。

在清单中:

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

您要创建文件的位置:

int permission = ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

String[] PERMISSIONS_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE;

if (permission != PackageManager.PERMISSION_GRANTED)

     ActivityCompat.requestPermissions(MainActivity.this,PERMISSIONS_STORAGE, 1);


File myDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Test");

myDir.mkdirs();

try 

    String FILENAME = "myFile.txt";
    File file = new File (myDir, FILENAME);
    String string = "hello world!";
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(string.getBytes());
    fos.close();
 
 catch (IOException e) 
    e.printStackTrace();
 

【讨论】:

【参考方案11】:

旧的文件保存方式可能​​不适用于新版本的 android,从 android10 开始。

 fun saveMediaToStorage(bitmap: Bitmap) 
        //Generating a dummy file name
        val filename = "$System.currentTimeMillis().jpg"
 
        //Output stream
        var fos: OutputStream? = null
 
        //For devices running android >= Q
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) 
            //getting the contentResolver
            context?.contentResolver?.also  resolver ->
 
                //Content resolver will process the contentvalues
                val contentValues = ContentValues().apply 
 
                    //putting file information in content values
                    put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
                    put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
                    put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
                
 
                //Inserting the contentValues to contentResolver and getting the Uri
                val imageUri: Uri? =
                    resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
 
                //Opening an outputstream with the Uri that we got
                fos = imageUri?.let  resolver.openOutputStream(it) 
            
         else 
            //These for devices running on android < Q
            //So I don't think an explanation is needed here
            val imagesDir =
                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
            val image = File(imagesDir, filename)
            fos = FileOutputStream(image)
        
 
        fos?.use 
            //Finally writing the bitmap to the output stream that we opened 
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
            context?.toast("Saved to Photos")
        
    

参考-https://www.simplifiedcoding.net/android-save-bitmap-to-gallery/

【讨论】:

【参考方案12】:

这段代码运行良好,也适用于 KitKat。欣赏@RajaReddy PolamReddy 在此处添加了更多步骤,并且还可以在图库中显示。

public void SaveOnClick(View v)
File mainfile;
String fpath;


    try 
//i.e  v2:My view to save on own folder     
        v2.setDrawingCacheEnabled(true);
//Your final bitmap according to my code.
        bitmap_tmp = v2.getDrawingCache();

File(getExternalFilesDir(Environment.DIRECTORY_PICTURES)+File.separator+"/MyFolder");

          Random random=new Random();
          int ii=100000;
          ii=random.nextInt(ii);
          String fname="MyPic_"+ ii + ".jpg";
            File direct = new File(Environment.getExternalStorageDirectory() + "/MyFolder");

            if (!direct.exists()) 
                File wallpaperDirectory = new File("/sdcard/MyFolder/");
                wallpaperDirectory.mkdirs();
            

            mainfile = new File(new File("/sdcard/MyFolder/"), fname);
            if (mainfile.exists()) 
                mainfile.delete();
            

              FileOutputStream fileOutputStream;
        fileOutputStream = new FileOutputStream(mainfile);

        bitmap_tmp.compress(CompressFormat.JPEG, 100, fileOutputStream);
        Toast.makeText(MyActivity.this.getApplicationContext(), "Saved in Gallery..", Toast.LENGTH_LONG).show();
        fileOutputStream.flush();
        fileOutputStream.close();
        fpath=mainfile.toString();
        galleryAddPic(fpath);
     catch(FileNotFoundException e)
        e.printStackTrace();
     catch (IOException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
    


这是在画廊中可见的媒体扫描仪。

private void galleryAddPic(String fpath) 
    Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
    File f = new File(fpath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);

【讨论】:

【参考方案13】:

Click Here获取完整描述和源代码

public void saveImage(Context mContext, Bitmap bitmapImage) 

  File sampleDir = new File(Environment.getExternalStorageDirectory() + "/" + "ApplicationName");

  TextView tvImageLocation = (TextView) findViewById(R.id.tvImageLocation);
  tvImageLocation.setText("Image Store At : " + sampleDir);

  if (!sampleDir.exists()) 
      createpathForImage(mContext, bitmapImage, sampleDir);
   else 
      createpathForImage(mContext, bitmapImage, sampleDir);
  

【讨论】:

在此处添加一些描述

以上是关于Android将文件保存到外部存储的主要内容,如果未能解决你的问题,请参考以下文章

Android - 将原始资产中的文件保存到外部存储以供其他应用访问,导致找不到内容根异常

Android 10:我都有哪些选择可以将外部存储上的文件保存到名为“/sdcard/my-app/”的目录中

Android 11 拍照+录制视频保存到外部共享区域

React Native:如何创建文本文件并将其保存到外部存储

Android 文件保存

将Android中的位图保存为外部存储中的JPEG文件夹