¿Cómo se escribe en una carpeta en una tarjeta SD en Android?

83

Estoy usando el siguiente código para descargar un archivo de mi servidor y luego escribirlo en el directorio raíz de la tarjeta SD, todo funciona bien:

package com.downloader;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.os.Environment;
import android.util.Log;

public class Downloader {

    public void DownloadFile(String fileURL, String fileName) {
        try {
            File root = Environment.getExternalStorageDirectory();
            URL u = new URL(fileURL);
            HttpURLConnection c = (HttpURLConnection) u.openConnection();
            c.setRequestMethod("GET");
            c.setDoOutput(true);
            c.connect();
            FileOutputStream f = new FileOutputStream(new File(root, fileName));

            InputStream in = c.getInputStream();

            byte[] buffer = new byte[1024];
            int len1 = 0;
            while ((len1 = in.read(buffer)) > 0) {
                f.write(buffer, 0, len1);
            }
            f.close();
        } catch (Exception e) {
            Log.d("Downloader", e.getMessage());
        }

    }
}

Sin embargo, usar Environment.getExternalStorageDirectory();significa que el archivo siempre se escribirá en la raíz /mnt/sdcard. ¿Es posible especificar una carpeta determinada para escribir el archivo?

Por ejemplo: /mnt/sdcard/myapp/downloads

RailsSon
fuente
12
También es necesario recordar agregar los permisos en el manifiesto para que esto funcione .. <uses-allow android: name = "android.permission.WRITE_EXTERNAL_STORAGE" />
timemirror

Respuestas:

160
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File (sdCard.getAbsolutePath() + "/dir1/dir2");
dir.mkdirs();
File file = new File(dir, "filename");

FileOutputStream f = new FileOutputStream(file);
...
plugmind
fuente
1
Asegúrese de utilizar todas las letras minúsculas en su directorio y nombres de archivo.
BeccaP
1
¿Puedo saber qué significa "/ dir1 / dir2" y sin esto no puedo guardar mi archivo en sdcard ah?
AndroidOptimist
17
Según el documento: Nota: no se confunda con la palabra "externo" aquí. Este directorio se puede considerar mejor como medio / almacenamiento compartido. Es un sistema de archivos que puede contener una cantidad relativamente grande de datos y que se comparte entre todas las aplicaciones (no impone permisos). Tradicionalmente, esta es una tarjeta SD , pero también se puede implementar como almacenamiento integrado en un dispositivo que es distinto del almacenamiento interno protegido y se puede montar como un sistema de archivos en una computadora
mr5
1
@NeonWarge ver aquí
mr5
Advertencia: getExternalStorageDirectory()quedó obsoleto en API 29.
Josh Correia
29

Agregar permiso al manifiesto de Android

Agregue este permiso WRITE_EXTERNAL_STORAGE a su manifiesto de aplicaciones.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="your.company.package"
    android:versionCode="1"
    android:versionName="0.1">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <!-- ... -->
    </application>
    <uses-sdk android:minSdkVersion="7" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest> 

Verifique la disponibilidad de almacenamiento externo

Siempre debe verificar primero la disponibilidad. Un fragmento de la documentación oficial de Android sobre almacenamiento externo .

boolean mExternalStorageAvailable = false;
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();

if (Environment.MEDIA_MOUNTED.equals(state)) {
    // We can read and write the media
    mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
    // We can only read the media
    mExternalStorageAvailable = true;
    mExternalStorageWriteable = false;
} else {
    // Something else is wrong. It may be one of many other states, but all we need
    //  to know is we can neither read nor write
    mExternalStorageAvailable = mExternalStorageWriteable = false;
}

Utilice un redactor de archivos

Por último, pero no menos importante, olvídate de FileOutputStreamy usa a FileWriteren su lugar. Más información sobre esa clase forma el javadoc FileWriter . Es posible que desee agregar más control de errores aquí para informar al usuario.

// get external storage file reference
FileWriter writer = new FileWriter(getExternalStorageDirectory()); 
// Writes the content to the file
writer.write("This\n is\n an\n example\n"); 
writer.flush();
writer.close();
hcpl
fuente
1
Los autores del libro que estoy leyendo recomiendan usar un FileOutputStream(bueno, no cubre las alternativas). ¿Por qué es mejor usar el FileWriteren su lugar? ¿Es más rápido / más confiable / algo más?
aga
3
@aga FileWritertiene métodos convenientes para escribir cadenas directamente, que no están presentes en un FileOutputStream. Si no está escribiendo archivos de texto, FileWriterno es de mucha ayuda. De hecho, si está escribiendo un archivo de imagen , en realidad no se puede hacer con un FileWriter Por supuesto, si realmente desea una buena API para texto, envuelva su FileOutputStreamen PrintStreamy tendrá los mismos métodos que System.out.
Ian McLaird
1
¿Por qué no puedo encontrar el archivo en mi tarjeta SD después de que se haya escrito correctamente?
Alguien en algún lugar
2

Encontré la respuesta aquí: http://mytechead.wordpress.com/2014/01/30/android-create-a-file-and-write-to-external-storage/

Dice,

/**

* Method to check if user has permissions to write on external storage or not

*/

public static boolean canWriteOnExternalStorage() {
   // get the state of your external storage
   String state = Environment.getExternalStorageState();
   if (Environment.MEDIA_MOUNTED.equals(state)) {
    // if storage is mounted return true
      Log.v("sTag", "Yes, can write to external storage.");
      return true;
   }
   return false;
}

y luego usemos este código para escribir en el almacenamiento externo:

// get the path to sdcard
File sdcard = Environment.getExternalStorageDirectory();
// to this path add a new directory path
File dir = new File(sdcard.getAbsolutePath() + "/your-dir-name/");
// create this directory if not already created
dir.mkdir();
// create the file in which we will write the contents
File file = new File(dir, "My-File-Name.txt");
FileOutputStream os = outStream = new FileOutputStream(file);
String data = "This is the content of my file";
os.write(data.getBytes());
os.close();

Y esto es todo. Si ahora visita su carpeta / sdcard / your-dir-name /, verá un archivo llamado - My-File-Name.txt con el contenido especificado en el código.

PD: - Necesita el siguiente permiso -

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
alquimista
fuente
2
El directorio /sdcard/no siempre es la sdcard.
SilverCorvus
Estoy de acuerdo con Curly. En mi teléfono, el método getExternalStorageDirectory devuelve un almacenamiento emulado, que no es la tarjeta sd.
Spindizzy
-1

Para descargar un archivo para descargar o carpeta de música en la tarjeta SD

File downlodDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);// or DIRECTORY_PICTURES

Y no olvide agregar estos permisos en el manifiesto

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
AndroidGeek
fuente
¿No se descarga esto en la carpeta Descargas en lugar de en una tarjeta SD?
Josh Correia