Android将文件保存到外部存储

时间:2011-10-25 09:12:13

标签: android file storage external

我在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时?

12 个答案:

答案 0 :(得分:177)

使用此功能将位图保存在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

答案 1 :(得分:24)

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);
                }
    });

}

答案 2 :(得分:5)

您需要获得此权限

< 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
}

答案 3 :(得分:4)

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

它应该在您的清单文件中看起来像这样:

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

答案 4 :(得分:4)

试试这个:

  1. 检查外部存储设备
  2. 写文件
  3. 阅读文件
  4. 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://stackoverflow.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");
        }
    }
    

答案 5 :(得分:3)

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

关于文件的可见性:如果您在目录中放置了名为.nomedia的文件,则告诉Android您不希望它扫描媒体文件,它们不会出现在图库中。

答案 6 :(得分:3)

Update 2018,SDK> = 23。

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

class Orders extends Component {
    let cancel;
    state = {
        orders: [],
        loading: true
    }

    componentDidMount() {
        this.asyncRequest = api.get('/orders.json', {
        cancelToken: new CancelToken(function executor(c) {
            // An executor function receives a cancel function as a parameter
            cancel = c;
            })
        })
            .then(response => {
                const fetchedOrders = [];
                if (response && response.data) {
                    for (let key in response.data) {
                        fetchedOrders.push({
                            id: key,
                            ...response.data[key]
                        });
                    }
                }
                this.setState({ loading: false, orders: fetchedOrders });
            })
            .catch(error => {
                this.setState({ loading: false });
                // please check the syntax, I don't remember if it is throw or throw new
                throw error;
            });
    }

    componentWillUnmount() {
       if (this.asyncRequest) {
          cancel();
       }
    }

    render() {
        return (
            <div>
                {this.state.orders.map(order => {
                    return (<Order
                        key={order.id}
                        ingrediencies={order.ingrediencies}
                        price={order.price} />);
                })}
            </div>
        );
    }
}

,当然还要添加<ErrorHandler> <Orders /> </ErrorHandler>

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);
    }
}

答案 7 :(得分:2)

因为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;
        }

答案 8 :(得分:1)

对于API级别23(棉花糖)及更高版本,除了清单中的uses-permission外,还应实现弹出权限,并且用户需要在运行时使用该应用时授予它。

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

在清单中:

<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();
 }

答案 9 :(得分:1)

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

 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/

答案 10 :(得分:0)

这段代码非常好用也在使用KitKat。感谢@RajaReddy PolamReddy
此处添加了更多步骤,并且还在Gallery上添加了Visible。

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();
    }

}

这是Media Gallery中可见的媒体扫描程序。

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);
}

答案 11 :(得分:0)

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);
  }
}