Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

기록장

[안드로이드] 내부(Internal), 외부(External) 저장소, File read/write 본문

안드로이드

[안드로이드] 내부(Internal), 외부(External) 저장소, File read/write

edit0 2021. 1. 20. 23:16

안드로이드 기기의 파일 저장 공간은 내부(Internal) 저장소외부(External) 저장소로 나뉜다.

 

내부 저장소는 안드로이드 시스템 도구, 앱 데이터 등을 위한 공간으로 안드로이드의 보안 모델에 따라 보호를 받는 공간이다. 그래서 저장소 내부는 관련된 하나의 앱만 접근이 가능하고 파일을 읽고 쓰기가 가능하다. 당연히 앱을 지우게 될 경우 내부 저장소 데이터들도 함께 지워진다.

 

외부 저장소는 기기에 따라 SD카드, 기기 자체 저장소로 제공한다. 그래서 모든 기기에서 외부 저장소를 사용할 수 있는 것은 아니다. 이 공간을 사용할 시 다른 앱에서도 이 앱에서 만든 외부 저장소에 접근할 수 있고, 사용자가 기기 폴더에 직접 접근도 가능하다. 그리고 앱 삭제 시 앱과 함께 외부 저장소 파일은 지워지지 않는다. 쉽게 생각하면 도서관에 내 책을 기부했다고 가정했을 때 그 책을 누구나 볼 수 있고 내가 도서관을 가지 않아도 책은 그대로 남아있다.

 

 

파일을 읽고 쓰는데 있어서 필요한 메소드이다.

- File: 파일 및 디렉터리를 지칭하는 클래스

- FileInputStream: 파일에서 바이트 데이터를 읽기 위한 함수 제공

- FileOutputStream: 파일에 바이트 데이터를 쓰기 위한 함수 제공

- FileReader: 파일에서 문자열 데이터를 읽기 위한 함수 제공

- FileWriter: 파일에 문자열 데이터를 쓰기 위한 함수 제공

 

저장소와 관련된 정보는 Environment 클래스로 얻을 수 있다.

- Environment.getExternalStorageState(): 외부 저장 공간 상태

- Environment.getExternalStorageDirectory().getAbsolutePath(): 외부 저장 공간 경로 (애뮬레이터 기준 /sdcard or /storage/emulator/0)

- Environment.getDataDirectory().getAbsolutePath(): 내부 저장 공간 경로 (애뮬레이터 기준 /data/data/패키지명/files)

* 메모리 저장 경로는 기기마다 다를 수 있다.

 

- Environment.getExternalStoragePublicDirectory(): 기기에 설정된 공용 폴더의 경로를 전달해줌

 

그 외 (공용 폴더)

- Environment.DIRECTORY_ALARMS: 알람으로 사용할 오디오 파일 저장 폴더

- Environment.DIRECTORY_DCIM: 카메라로 촬영한 사진 저장 폴더

- Environment.DIRECTORY_DOWNLOADS: 다운로드한 파일 저장 폴더

- Environment.DIRECTORY_MUSIC: 음악 파일 저장 폴더

- Environment.DIRECTORY_MOVIES: 영상 파일 저장 폴더
- Environment.DIRECTORY_NOTIFICATIONS: 알림음으로 사용할 오디오 파일 저장 폴더

- Environment.DIRECTORY_PICTURES: 이미지 파일 저장 폴더

 

 

내부 저장소에 접근하기 위해서는 특별한 권한이 필요하지 않다. 그러나 외부 저장소는 다른 앱의 외부 저장소도 건드릴 수 있으므로 위험 권한으로 분류되어 사용자에게 권한 허가를 받아야 한다.

 

먼저 퍼미션을 추가해야한다.

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

외부 저장소에 파일을 읽고 쓰겠다는걸 선언해주는 것이다. 그러나 앞서 언급 한대로 위험 권한이므로 사용자에게 권한 허가를 받아야 한다.

 

API 29 이상부터는 Manifest.xml의 application 내에 추가

android:requestLegacyExternalStorage="true"

 

checkPermissions 메소드를 호추라여 권한 여부를 알아보고 권한 허가를 받기 위한 창을 띄운다.

public void checkPermissions(String[] permissions){
        ArrayList<String> targetList = new ArrayList<>();

        for(int i=0;i<permissions.length;i++){
            String curPermission = permissions[i]; //확인할 권한을 curPermission에 넣고
            int permissionCheck = ContextCompat.checkSelfPermission(this, curPermission); //승인 여부 확인하여 int형 반환

            if(permissionCheck == PackageManager.PERMISSION_GRANTED){
                Log.i("태그", curPermission+"대한 권한 있음");
            }
            else{
                Log.i("태그", curPermission+"대한 권한 없음");
                targetList.add(curPermission);
            }
        }

        String[] targets = new String[targetList.size()];
        targetList.toArray(targets);

        if(targets.length > 0) {
            ActivityCompat.requestPermissions(this, targets, 1);
        }
}

잘 이해가 가지 않는다면 참고

edit0.tistory.com/25

 

[안드로이드] 일반 권한, 위험 권한

권한은 앱 내에서 단말에 대한 기능들을 사용할 수 있도록 허가를 해주는 것으로 일반 권한과 위험 권한으로 나뉜다. 이름에서와 같이 일반보다 위험이 더 뭔가 중요한 정보를 다루는 느낌이 든

edit0.tistory.com

 

외부저장소 전체 코드

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button1"
        android:text="폴더 만들기"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edittext1"
        android:hint="제목"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edittext2"
        android:hint="저장할 내용 입력"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button2"
        android:text="파일 저장"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button3"
        android:text="파일 불러오기"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="파일 리스트 불러오기"
        android:id="@+id/button4"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textview1"
        android:textSize="20dp"
        android:background="#FFEB3B"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textview2"
        android:textSize="20dp"
        android:background="#CDDC39"/>

</LinearLayout>

 

MainActivity.java

public class MainActivity extends AppCompatActivity {

    String[] permissions = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; //퍼미션 받을 목록

    String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/my_folder"; //경로 지정

    File file;
    Button b1, b2, b3, b4;
    EditText et1, et2;
    TextView tv1, tv2;

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

        checkPermissions(permissions); //권한 확인 및 허가 받기

        b1 = findViewById(R.id.button1);
        b2 = findViewById(R.id.button2);
        b3 = findViewById(R.id.button3);
        b4 = findViewById(R.id.button4);
        et1 = findViewById(R.id.edittext1);
        et2 = findViewById(R.id.edittext2);
        tv1 = findViewById(R.id.textview1);
        tv2 = findViewById(R.id.textview2);

        //폴더 만들기
        b1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                File mk_folder = new File(path);

                if (!mk_folder.exists()) { //같은 파일이 없다면
                    mk_folder.mkdir(); //파일 생성
                    tv1.append("폴더 생성됨 \n");
                }
            }
        });

        //파일 만들고 입력된 내용 저장하기
        b2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    file = new File(path + "/" + et1.getText().toString());
                    FileOutputStream fileOutputStream= new FileOutputStream(file, true);

                    if(file.length() != 0){ //파일이 있다면 제거하고 다시 만들기
                        file.delete();
                        fileOutputStream= new FileOutputStream(file, true);
                    }

                    byte[] text = et2.getText().toString().getBytes(); //바이트 데이터로
                   fileOutputStream.write(text); //쓰기
                    fileOutputStream.close();

                    tv1.append("파일 생성됨 \n");

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

        //저장한 파일 내용 읽어오기
        b3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    FileInputStream fileInputStream = new FileInputStream(file);

                    byte[] text = new byte[fileInputStream.available()]; //바이트로 받기
                    fileInputStream.read(text);
                    tv1.append(new String(text) + "\n");
                    fileInputStream.close();

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

        //폴더 내에 파일 리스트 보기
        b4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                File[] filelist = new File(path).listFiles();

                for(int i=0;i<filelist.length;i++){
                    if(filelist[i].isFile() == true){
                        tv2.append("파일 - "+ filelist[i] + "\n");
                    }
                    else if(filelist[i].isDirectory() == true){
                        tv2.append("폴더 - " + filelist[i] + "\n");
                    }
                }


            }
        });



    }

    public void checkPermissions(String[] permissions){
        ArrayList<String> targetList = new ArrayList<>();

        for(int i=0;i<permissions.length;i++){
            String curPermission = permissions[i]; //확인할 권한을 curPermission에 넣고
            int permissionCheck = ContextCompat.checkSelfPermission(this, curPermission); //승인 여부 확인하여 int형 반환

            if(permissionCheck == PackageManager.PERMISSION_GRANTED){
                Log.i("태그", curPermission+"대한 권한 있음");
            }
            else{
                Log.i("태그", curPermission+"대한 권한 없음");
                targetList.add(curPermission);
            }
        }

        String[] targets = new String[targetList.size()];
        targetList.toArray(targets);

        if(targets.length > 0) {
            ActivityCompat.requestPermissions(this, targets, 1);
        }

    }
}

 

결과

 

Device File Explorer 에서 확인할 수 있다.

 

다음은 내부 저장소 코드

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edittext1"
        android:hint="제목"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edittext2"
        android:hint="저장할 내용 입력"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button1"
        android:text="파일 저장"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button2"
        android:text="파일 불러오기"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textview1"
        android:textSize="20dp"
        android:background="#FFEB3B"/>

</LinearLayout>

 

MainActivity.java

public class MainActivity extends AppCompatActivity {

    Button b1, b2;
    EditText et1, et2;
    TextView tv1;

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

        b1 = findViewById(R.id.button1);
        b2 = findViewById(R.id.button2);
        et1 = findViewById(R.id.edittext1);
        et2 = findViewById(R.id.edittext2);
        tv1 = findViewById(R.id.textview1);

        //파일 쓰기
        b1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    FileOutputStream fileOutputStream = openFileOutput(et1.getText().toString(), MODE_PRIVATE);
                    String str = et2.getText().toString();

                    byte[] text = str.getBytes();
                    fileOutputStream.write(text);
                    fileOutputStream.close();
                    tv1.append("파일 생성됨" + "\n");
                }catch (Exception e){}
            }
        });

        //파일 읽기
        b2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    FileInputStream fileInputStream = openFileInput(et1.getText().toString());

                    byte[] text = new byte[fileInputStream.available()];
                    fileInputStream.read(text);
                    tv1.append(new String(text) + "\n");
                    fileInputStream.close();

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

    }
}

 

결과

 

 

/data/data/패키지명/files 내부로 들어가서 확인할 수 있다.

 

 

감사합니다.