错误:打开失败:ENOENT(没有这样的文件或目录)

76
我试图创建一个用于保存照片的文件,但是我无法创建该文件。然而,我真的找不到错误所在。你能看一下并给我一些建议吗?
    private File createImageFile(){
            File imageFile=null;
            String stamp=new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            File dir= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
            String imageFileName="JPEG_"+stamp+"_";
            try {
                imageFile=File.createTempFile(imageFileName,".jpg",dir);
            } catch (IOException e) {
                Log.d("YJW",e.getMessage());
            }
            return  imageFile;
        }

我已经添加了权限。

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

这种方法总是会出现如下错误:

打开失败: ENOENT(没有这个文件或目录)


2
API参考文档中给出的示例有这样一行代码:// Make sure the Pictures directory exists. path.mkdirs();。你确定该目录已经存在了吗? - Matt Gibson
谢谢!在培训中没有这样的提示。我应该搜索API参考文档。谢谢! - Jiawei Yang
@JiaweiYang 是我的建议起作用了吗?如果是的话,我会将其添加为答案。 - Matt Gibson
因为 Android 10(API 级别 29) 及更高版本不允许直接在外部存储中创建文件夹,您应该使用 getExternalFilesDir(Environment.DIRECTORY_PICTURES)。 - Samir Alakbarov
12个回答

72

图片目录可能尚不存在,不保证存在。

getExternalStoragePublicDirectory()的API文档中,该代码使用mkdirs确保目录存在:

File path = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES);
File file = new File(path, "DemoPicture.jpg");

try {
    // Make sure the Pictures directory exists.
    path.mkdirs(); 

因此,您只需在现有代码中调用path.mkdirs(),然后再调用createTempFile即可。


需要检查新目录是否已经创建,如果没有则执行以下操作: if (!path.mkdirs()) { Log.d("不可用:" , path.getAbsolutePath()); } - Kiran Maniya
2
getExternalStoragePublicDirectory(...)已被弃用。请使用this.getApplicationContext().getExternalFilesDir(null).toString();代替。 - Moonis Abidi

20

当用户从图库选择文件时,不能保证所选文件是由其他应用添加或编辑的。因此,如果用户选择了属于另一个应用程序的文件,则可能会遇到权限问题。解决这个问题的一种快速方法是将以下代码添加到AndroidManifest.xml文件中:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<manifest ... >
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

注意:对于Android 11,请参阅范围存储执行策略https://developer.android.com/about/versions/11/privacy/storage


2
很遗憾,它不支持API 30级。 - Mattia
Android 11系统忽略了requestLegacyExternalStorage标志,改为使用Scope Storage。如果需要进行数据合并/移动,请在Android 11中使用android:preserveLegacyExternalStorage="true"。有关Scope Storage的详细信息,请参阅以下链接:https://developer.android.com/about/versions/11/privacy/storage 和 https://developer.android.com/training/data-storage#scoped-storage。 - Bhavesh Moradiya
当用户将其Android操作系统更新到11版本时,android:preserveLegacyExternalStorage="true"对于您现有的应用程序是必需的。通过这个设置,您可以在11版本的范围存储策略下将数据从旧目录传输到新目录。 - Bhavesh Moradiya

13
Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES)

用:

private File createImageFile() throws IOException {
        // Create an image file name

请确保您拨打:

mkdirs() // and not mkdir()

以下是应该适用于您的代码:

        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = new File(Environment.getExternalStorageDirectory().toString(), "whatever_directory_existing_or_not/sub_dir_if_needed/");
        storageDir.mkdirs(); // make sure you call mkdirs() and not mkdir()
        File image = File.createTempFile(
                imageFileName,  // prefix
                ".jpg",         // suffix
                storageDir      // directory
        );

        // Save a file: path for use with ACTION_VIEW intents

        mCurrentPhotoPath = "file:" + image.getAbsolutePath();
        Log.e("our file", image.toString());
        return image;
    }

我在遵循Android Studio文档中提供的示例时遇到了一些问题,后来发现在stackoverflow上有很多人也遇到了同样的问题。这是因为即使我们设置了

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

一些设备仍存在这个问题。

我的经验是,当我在调试模式下尝试示例时,它可以正常工作,在进行了另外三次测试后,我的SD卡突然损坏了,但我不认为这与他们的示例有关(有趣)。我购买了一个新的SD卡并再次尝试,结果发现无论是发布模式还是调试模式都记录了相同的错误日志:目录不存在 ENOENT。最终,我不得不自己创建包含从手机相机中拍摄的图片的目录。结果证明,我是正确的,它完美地工作了。

我希望这能帮助你和其他正在寻找答案的人。


1
它在Android 11上无法工作。 - mhammad alkhalaf

9
一个快速解决方法是在AndroidManifest.xml文件中添加以下代码:
<manifest ... >
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

注意:适用于API级别29或更高级别。

7
这是针对API 29的临时解决方案,仅在API 30上不起作用。 - Ridha Rezzag

5
我使用了contentResolver和URI,这对我很有用。我在另一个SO帖子中看到的,但现在找不到了。
private String getRealPathFromURI(Uri contentURI) {
    String result;
    Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
    if (cursor == null) {
        result = contentURI.getPath();
    } else {
        cursor.moveToFirst();
        int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
        result = cursor.getString(idx);
        cursor.close();
    }
    return result;
}

希望这能帮到你......

2
我是这样解决的:
        public Intent getImageCaptureIntent(File mFile) {
             Intent mIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
             Uri photoURI = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", mFile);
             mIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);

             // The tip is this code below
             List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(mIntent, PackageManager.MATCH_DEFAULT_ONLY);
                 for (ResolveInfo resolveInfo : resInfoList) {
                      String packageName = resolveInfo.activityInfo.packageName;
                      grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                 }

             return  mIntent;
        }

1

如果您正在使用 Kotlin,则可以使用以下函数。您需要提供一个用于存储图像的路径,一个位图(在本例中为图像),如果您想要降低图像质量,则提供百分比,即 50%。

fun cacheLocally(localPath: String, bitmap: Bitmap, quality: Int = 100) {
        val file = File(localPath)
        file.createNewFile()
        val ostream = FileOutputStream(file)
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, ostream)
        ostream.flush()
        ostream.close()
    }

希望它能够正常工作。

3
Kotlin与否在这里并不重要。 - Cuong Vo

1
File dirPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File imageFile = new File(dirPath, "YourPicture.jpg");

try {
    if(!dirPath.isDirectory()) {
       dirPath.mkdirs(); 
    } 
    imageFile.createNewFile();

} catch(Exception e) {
    e.printStackTrace();
}

0
尝试这个:
private File createImageFile() throws IOException {  

    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());  

    String imageFileName="JPEG_"+stamp+".jpg";  

    File photo = new File(Environment.getExternalStorageDirectory(),  imageFileName);  

    return photo;
}  

0

以下是我发现的修复方法 首先在你的AndroidManifest文件中添加这两行代码

然后在setContentView方法之后添加下面这行代码

ActivityCompat.requestPermissions(FullImageActivity.this,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_CODE);

用以下代码保存图像到相册

private void SaveImageToGallery() {
        BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
        Bitmap bitmap = drawable.getBitmap();
        FileOutputStream outputStream = null;
        File file = Environment.getExternalStorageDirectory();
        File dir = new File(file.getAbsolutePath()+"/folderName");
        dir.mkdirs();
        String filename = String.format("%d.jpg",System.currentTimeMillis());
        File outfile = new File(dir,filename);
        try{
            outputStream = new FileOutputStream(outfile);
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
            outputStream.flush();
            outputStream.close();
        }catch(Exception e){
            Log.d("SavingError", "SaveImageToGallery: "+e.getMessage());
        }
        Toast.makeText(this, "Image saved in folderName folder", Toast.LENGTH_SHORT).show();
    }

而对于Gif和动态Webp呢?这些类型将被转换为静态图像。 - CrackerKSR

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