You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio Java:Firebase Storage文件存外部存储报错求助

问题分析与修复方案

错误原因

你遇到的ENOENT (no such file or directory)错误,核心问题集中在路径处理、目录创建、权限缺失三个方面:

  • 直接使用相对路径Pictures/MediaVerse/,未关联外部存储根目录,系统无法定位到正确位置
  • 创建文件前未检查并创建父目录,目录不存在时无法写入文件
  • 重复定义File file变量,覆盖了原有路径逻辑
  • 未处理Android 6.0+的动态存储权限,以及Android 10+的分区存储适配
  • filename.replaceAll(":", ".")未重新赋值,String是不可变类型,该操作不会修改原变量

修复步骤与完整代码

1. 添加权限声明

在AndroidManifest.xml中添加必要权限:

<!-- Android 10及以下读写权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
<!-- Android 11+访问公共目录可选权限,也可使用MediaStore方案替代 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />

2. 动态申请权限

在启动保存逻辑前,先检查并申请存储权限,避免权限不足导致写入失败。

3. 修正路径与文件创建逻辑

关联外部存储根目录,创建文件前确保父目录存在;修复变量重复定义问题;正确处理文件名。

4. 优化资源释放

确保输入输出流在finally块中关闭,避免资源泄漏。


完整修改后的代码

public class MainActivity extends AppCompatActivity {

    ImageView imv;
    private static final int REQUEST_STORAGE_PERMISSION = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imv = findViewById(R.id.ff);
        Button btn = findViewById(R.id.xyz);

        // 检查存储权限
        if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION);
        }

        btn.setOnClickListener(view -> FirebaseDatabase.getInstance()
                .getReference("trial")
                .child("image")
                .addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot snapshot) {
                        String imageUrl = snapshot.getValue(String.class);
                        if (imageUrl != null && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                            saveImageToDevice(imageUrl);
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError error) {
                        Toast.makeText(MainActivity.this, "数据获取失败", Toast.LENGTH_SHORT).show();
                    }
                }));
    }

    private void saveImageToDevice(String url) {
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inSampleSize = 1;
        Bitmap bm = loadImage(url, bmOptions);
        if (bm == null) {
            Toast.makeText(this, "图片加载失败", Toast.LENGTH_SHORT).show();
            return;
        }
        imv.setImageBitmap(bm);

        // 获取外部存储公共图片目录
        File publicPictureDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        File targetDir = new File(publicPictureDir, "MediaVerse");

        // 创建目标目录(不存在则创建)
        if (!targetDir.exists()) {
            boolean dirCreated = targetDir.mkdirs();
            if (!dirCreated) {
                Toast.makeText(this, "目录创建失败", Toast.LENGTH_SHORT).show();
                return;
            }
        }

        // 生成文件名并处理特殊字符
        String filename = System.currentTimeMillis() + ".png";
        filename = filename.replaceAll(":", ".");

        File targetFile = new File(targetDir, filename);
        OutputStream outStream = null;

        try {
            outStream = new FileOutputStream(targetFile);
            bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
            outStream.flush();
            outStream.close();

            // 通知系统扫描文件,让其他应用能识别到该图片
            MediaScannerConnection.scanFile(this, new String[]{targetFile.getAbsolutePath()}, null,
                    (path, uri) -> Toast.makeText(MainActivity.this, "图片已保存:" + path, Toast.LENGTH_LONG).show());

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Toast.makeText(this, "文件未找到:" + e.getMessage(), Toast.LENGTH_LONG).show();
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "写入失败:" + e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    private InputStream openHttpConnection(String strURL) throws IOException {
        InputStream inputStream = null;
        URL url = new URL(strURL);
        URLConnection conn = url.openConnection();

        try {
            HttpURLConnection httpConn = (HttpURLConnection) conn;
            httpConn.setRequestMethod("GET");
            httpConn.connect();

            if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                inputStream = httpConn.getInputStream();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return inputStream;
    }

    private Bitmap loadImage(String URL, BitmapFactory.Options options) {
        Bitmap bitmap = null;
        InputStream in = null;
        try {
            in = openHttpConnection(URL);
            bitmap = BitmapFactory.decodeStream(in, null, options);
        } catch (IOException e1) {
            Toast.makeText(this, "图片下载失败:" + e1.getMessage(), Toast.LENGTH_SHORT).show();
        } finally {
            try {
                if (in != null) in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }

    // 处理权限申请结果
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_STORAGE_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "权限已获取", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "需要存储权限才能保存图片", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

额外说明

  • Android 10+分区存储适配:如果应用目标SDK为30+,推荐使用MediaStore API保存图片到公共目录,无需申请MANAGE_EXTERNAL_STORAGE权限(该权限需谷歌审核)。
  • 图片下载优化:手动处理Http连接易出现异常,建议使用Firebase Storage SDK的getFile()方法直接下载文件,或用Glide库加载并保存图片,稳定性更高。

内容的提问来源于stack exchange,提问作者Souvik Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 21:45:47