如何让用户访问我的应用程序的内部存储目录?

Posted

技术标签:

【中文标题】如何让用户访问我的应用程序的内部存储目录?【英文标题】:How can I let users access the internal storage directory of my app? 【发布时间】:2014-12-22 04:15:40 【问题描述】:

我的应用程序将文件存储在其内部存储目录中(/android/data/com.mycompany.myapp/files,由 getFilesDir() 返回),我希望允许用户直接从文件管理中访问这些文件移动设备上的应用程序或 Android 文件传输桌面应用程序。

Storage Options developer guide 说:

默认情况下,保存到内部存储的文件对您的应用程序是私有的,其他应用程序无法访问它们(用户也不能)。

“默认”意味着我可以更改默认权限以允许用户访问这些文件,但我找不到任何有关如何执行此操作的文档。目前,当我使用文件管理应用浏览 /Android/data 目录时,com.mycompany.myapp 目录处于隐藏状态。

我目前正在像这样保存文件数据:

String fileName = "myfile.jpg";
String filePath = getFilesDir() + File.separator + fileName;
FileOutputStream fileStream = new FileOutputStream(filePath);
fileData.writeTo(fileStream); // fileData is a ByteArrayOutputStream
fileStream.close();

我尝试将单个文件的权限设置为全球可读,但这并没有使目录可见:

FileOutputStream fileStream = this.app.openFileOutput(fileName, Context.MODE_WORLD_READABLE);

我还检查了 AndroidManifest 文件的文档,但没有看到任何内容。有谁知道我该怎么做?

【问题讨论】:

我想知道您为什么要将文件存储在内部存储而不是外部存储中? 我希望文件与应用程序紧密连接,这样如果应用程序被删除,文件也会随之删除。而且在大多数情况下,用户不需要查看这些文件,因此将它们保存在 com.mycompany.myapp 目录中似乎是最干净的。但在某些情况下,用户直接访问这些文件会很方便。特别是,将一批文件添加到应用程序可能比通过应用程序界面进行拖放操作更容易。 如果用户有root权限,用户可以访问内部存储。 是的,但我不希望用户必须 root 他们的设备才能以这种方式访问​​他们的文件。其他应用似乎将文件暴露给普通用户,我也想这样做。 【参考方案1】:

我仔细查看了getFilesDir()getExternalFilesDir() 的结果,发现getFilesDir() 返回/data/data/[packagename]/filesgetExternalFilesDir() 返回/Android/data/[packagename]/files。我以为我在/Android/data 浏览的应用程序文件是内部存储目录,但现在我发现这些实际上是外部存储目录。

如果内部存储目录确实对普通用户不可用,我希望文档说,而不是说它们“默认”不可用。至少对我来说,说“默认”意味着可以更改默认行为。

无论如何,我测试并确认如果我删除我的应用程序,保存到getExternalFilesDir() 的文件会自动删除。因此,这满足了我对与应用程序明确连接的存储位置的需求(使用特定于应用程序的目录名称并随应用程序删除)但用户可以访问以进行偶尔的手动文件管理。

以下是可能对其他人阅读本文有所帮助的比较:

getFilesDir() - 创建一个特定于应用程序的目录;对用户隐藏;随应用删除 getExternalFilesDir() - 创建一个特定于应用程序的目录;用户可访问;随应用删除 getExternalStoragePublicDirectory() - 使用共享目录(例如,音乐);用户可访问;删除应用后仍然存在

【讨论】:

嘿...你知道为什么当我们将手机连接到 USB 端口时,我们无法从 Microsoft Windows 访问这些文件吗?对于getExternalStoragePublicDirectory(),我将图片保存在Environment.DIRECTORY_DCIM Camera 文件夹中......但是从我的应用程序拍摄的图片在Windows 中不可见(即使我激活了隐藏文件)【参考方案2】:

我认为您对开发人员文档感到困惑,我不能怪您,这不是我读过的最好的文档。无论如何,Android提供了两种将文件保存到文件系统的方式:

    使用内部存储 使用外部存储

内部存储始终可用,并且只有您的应用可以访问,系统中的其他应用无法访问您的应用保存到此分区的文件。

现在,我认为您需要的是外部存储,因为它是“全球可读的”,并且可以被任何人和任何任何应用程序访问。缺点是它可能不可用,因为用户可以将它安装在 USB 设备上 - 用户可以随时将其移除。

根据文档说明,您不应使用 MODE_WORLD_WRITEABLEMODE_WORLD_READABLE,因为它非常危险。此外,这些常量自 API 级别 17 起已被弃用

推荐

如果您需要公开您的文件,请将它们保存到外部存储。您需要在清单文件中声明一个权限,以避免您的应用在每次访问 外部存储...

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

由于外部存储可能不可用,您需要在执行任何操作之前确定它的状态,否则您的应用会崩溃...

public enum StorageState
  NOT_AVAILABLE, WRITEABLE, READ_ONLY


public StorageState getExternalStorageState() 
    StorageState result = StorageState.NOT_AVAILABLE;
    String state = Environment.getExternalStorageState();

    if (Environment.MEDIA_MOUNTED.equals(state)) 
        return StorageState.WRITEABLE;
    
    else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) 
        return StorageState.READ_ONLY;
    

    return result;

有更多关于文档和您应该注意的事项的信息。例如,您可以将这些文件的所有权授予您的应用程序,以便在卸载您的应用程序时系统可以自动删除这些文件。更多信息请参考documentation on the Android Developers Site

【讨论】:

内部/外部名称肯定令人困惑(设备内置内存上的“外部”存储与可移动存储卡上的“辅助外部”存储),但我想我明白了。内部存储位于/Android/data/com.mycompany.myapp,对吗?似乎其他应用程序正在让用户看到这一点。例如,我安装了 Kindle 应用程序,我可以从 ES File Explorer 应用程序或 Mac 上的 Android File Transfer 浏览 /Android/data/com.amazon.kindle/files 目录。 @arlomedia mate,我认为您变得更加困惑。您在文件夹中查看的 Kindle 文件不是私有的,它们是“世界可读的”。此外,如果您将 Windows 机器连接为 MTP,您甚至不需要使用 Android File Transfer 之类的东西来读取这些文件。所以,基本上,你并没有真正理解它,否则你不会发表评论...... MODE_PRIVATE 意味着只对拥有的应用程序可见,简单地说。继续浏览您的应用程序内部存储空间,您会发现其中许多是空的 @arlomedia 你到底有什么不明白的? 我的应用程序将其文件保存在 data/com.mycompany.myapp/files 中,这对于浏览文件系统的普通(非 root)用户来说是不可见的。确实会出现一些其他应用程序的目录,并且用户可以浏览他们的文件。我怎样才能让我的应用以这种方式运行? 我只是尝试保存到 getExternalFilesDir() 位置而不是 getFilesDir() 位置,这创建了一个可浏览的 data/com.mycompany.myapp/files 目录。所以看起来这些目录是各种应用程序的外部存储,而不是内部存储。这就是你想告诉我的吗?【参考方案3】:

将此添加到清单

然后将其添加到您的活动中:

private static final String RequieredPermission = Manifest.permission.READ_EXTERNAL_STORAGE;

然后调用HandlePermission() 您需要检查权限的地方,通常在活动的 Oncreate 方法中。然后需要这些函数:

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 
        Log.d("amir", "onRequestPermissionsResult: called");
        switch (requestCode) 
            case REQUEST_PERMISSION_READING_STATE:
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                    Toast.makeText(MainActivity.this, "Permission Granted!", Toast.LENGTH_SHORT).show();
                 else 
                    Toast.makeText(MainActivity.this, "Permission Denied!", Toast.LENGTH_SHORT).show();
                
        

    

    private void HandlePermission() 
        int permissionCheck = ContextCompat.checkSelfPermission(
                this, RequieredPermission);
        if (permissionCheck != PackageManager.PERMISSION_GRANTED) 
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    RequieredPermission)) 
                showExplanationaboutPermission("Permission Needed", "Rationale", RequieredPermission, REQUEST_PERMISSION_READING_STATE);
             else 
                requestPermission(RequieredPermission, REQUEST_PERMISSION_READING_STATE);
            
         else 
            Toast.makeText(MainActivity.this, "Permission (already) Granted!", Toast.LENGTH_SHORT).show();
        
    

    private void showExplanationaboutPermission(String title,
                                                String message,
                                                final String permission,
                                                final int permissionRequestCode) 
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(title)
                .setMessage(message)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() 
                    public void onClick(DialogInterface dialog, int id) 
                        requestPermission(permission, permissionRequestCode);
                    
                );
        builder.create().show();
    

    private void requestPermission(String permissionName, int permissionRequestCode) 
        ActivityCompat.requestPermissions(this,
                new String[]permissionName, permissionRequestCode);
    

【讨论】:

以上是关于如何让用户访问我的应用程序的内部存储目录?的主要内容,如果未能解决你的问题,请参考以下文章

将文件下载到内部存储 [重复]

如何从手机(外部/内部)存储中选择图像并上传到 sqlite 数据库?

如何使用 Unity C# 从 android 内部存储访问图像和 3D 对象

如何使用 Glide 将图像保存到内部存储?

Android:如何将位图写入内部存储

Android 中内部存储和外部存储的理解与应用