짱짱해커가 되고 싶은 나

08-1. 파일 처리 본문

모바일

08-1. 파일 처리

동로시 2021. 2. 22. 15:25

안드로이드 파일 처리 방법(제한된 폴더/SD 카드 등에서만 가능)

- Java에서 제공되는 파일 관련 클래스 사용

- 안드로이드에서 제공되는 파일 관련 클래스 사용

 

* 내장 메모리 파일 처리

ex) 앱을 종료했다가 다음에 다시 시작했을 때 사용했던 곳부터 이어서 작업하고 싶을 때 내장 메모리에 파일을 저장하고 읽어오는 방식

 

내장 메모리 : /data/data/패키지명/files 에 위치

(응용 프로그램마다 고유의 저장 공간 존재)

 

<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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

   <Button
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="내장 메모리에 파일 쓰기"
       android:id="@+id/btnWrite"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="내장 메모리에서 파일 읽기"
        android:id="@+id/btnRead"/>

</LinearLayout>

<MainActivity.java>

package com.example.toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Display;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

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

        Button btnRead, btnWrite;
        btnRead = (Button)findViewById(R.id.btnRead);
        btnWrite = (Button)findViewById(R.id.btnWrite);

        btnWrite.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    FileOutputStream outFs = openFileOutput("file.txt", Context.MODE_PRIVATE);
                    String str = "쿡북 안드로이드";
                    outFs.write(str.getBytes());
                    outFs.close();
                    Toast.makeText(getApplicationContext(), "file.txt가 생성됨", Toast.LENGTH_SHORT).show();
                }catch(IOException e){
                }
            }
        });

        btnRead.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    FileInputStream inFs = openFileInput("file.txt");
                    byte[] txt = new byte[30];
                    inFs.read(txt);
                    String str = new String(txt);
                    Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();
                }catch(IOException e){
                    Toast.makeText(getApplicationContext(), "파일 없음", Toast.LENGTH_SHORT).show();
                }
            }
        });

    }
}

파일 모드에서 쓰기는 MODE_PRIVATE, MODE_APPEND가 가능하다.

 

프로젝트1: 일기장 앱

날짜를 선택했을 때 그 날짜의 일기가 없으면 새로 작성, 있으면 보여주기

- DatePicker 1개

- EditText 1개

- Button 1개

 

<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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <DatePicker
        android:layout_weight="1"
        android:layout_marginTop="60dp"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/datePicker"
        android:datePickerMode="spinner"
        android:calendarViewShown="false"/>

    <EditText
        android:padding="5dp"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/getDiary"
        android:background="#F8E0E6"
        android:textColor="#000000"
        android:lines="8"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn"
        android:enabled="false"
        android:text="Button"/>

</LinearLayout>

<MainActivity.java>

package com.example.toast;

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;

public class MainActivity extends AppCompatActivity {
    DatePicker datePicker;
    EditText editText;
    Button btn;
    String fileName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setTitle("간단 일기장");

        datePicker = (DatePicker)findViewById(R.id.datePicker);
        editText = (EditText)findViewById(R.id.getDiary);
        btn = (Button)findViewById(R.id.btn);

        Calendar cal = Calendar.getInstance();
        int cYear = cal.get(Calendar.YEAR);
        int cMont = cal.get(Calendar.MONTH);
        int cDay = cal.get(Calendar.DAY_OF_MONTH);

        fileName = Integer.toString(cYear) + "_" + Integer.toString(cMont+1) + "_" +
                Integer.toString(cDay) + ".txt";
        String initStr = readDiary(fileName);
        editText.setText(initStr);
        btn.setEnabled(true);

        datePicker.init(cYear, cMont, cDay, new DatePicker.OnDateChangedListener() {
            @Override
            public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                fileName = Integer.toString(year) + "_" + Integer.toString(monthOfYear+1) + "_" +
                        Integer.toString(dayOfMonth) + ".txt";
                String str = readDiary(fileName);
                editText.setText(str);
                btn.setEnabled(true);
            }
        });

        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                try{
                    FileOutputStream outFs = openFileOutput(fileName, Context.MODE_PRIVATE);
                    String str = editText.getText().toString();
                    outFs.write(str.getBytes());
                    outFs.close();
                    Toast.makeText(getApplicationContext(), fileName + "이 저장됨", Toast.LENGTH_SHORT).show();
                }catch(IOException e){
                }
            }
        });
    }

    String readDiary(String fName){
        String diaryStr = null;
        FileInputStream inFs;
        try{
            inFs = openFileInput(fName);
            byte[] txt = new byte[500];
            inFs.read(txt);
            inFs.close();
            diaryStr = (new String(txt).trim());
            btn.setText("수정하기");
        } catch (IOException e){
            editText.setHint("일기 없음");
            btn.setText("새로 저장");
        }
        return diaryStr;
    }
}

 

프로젝트1

 

이렇게 저장된 파일을 확인하려면 장치 연결/AVD 실행 상태에서 Device File Explorer를 들어간다.

그리고 내장 메모리 파일 위치에 가면 만든 파일이 있는 것을 확인할 수 있다.

 

* raw 폴더 파일 처리

프로젝트의 /res/raw 폴더에 필요한 파일을 저장해서 사용할 수 있다. (raw 폴더를 생성할때 resource type도 raw)

읽기 전용으로만 사용 가능하고 쓰기는 내장 메모리나 SD 카드를 사용해야 한다.

 

package com.example.toast;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import androidx.appcompat.app.AppCompatActivity;

import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

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

        Button btn = (Button)findViewById(R.id.btn);
        final EditText editText = (EditText)findViewById(R.id.editText);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    InputStream inputStream = getResources().openRawResource(R.raw.raw_test);
                    byte[] txt = new byte[inputStream.available()];
                    inputStream.read(txt);
                    editText.setText(new String(txt));
                    inputStream.close();
                }catch (IOException e){

                }
            }
        });
    }

}

- openRawResource() : /res/raw/ 리소스를 읽기용으로 열고 InputStream 으로 반환한다.

- inputStream.availabe() : 입력 스트림에서 읽을 수 있는 바이트 수를 반환한다 (== 파일크기)

 

* SD 카드 파일 처리

SD 카드에서 파일을 읽기 위해 Device File Explorer에서 /sdcard 폴더 또는 /storage/emulator/0 폴더에 텍스트 파일을 올린다.

(AVD에서 SD 카드의 경로는 절대적인 것이 아니며 SDK버전에 따라 달라질 수 있다.)

AndroidMainfest.xml에 외부 저장 매체 쓰기 권한과 속성을 추가한다.

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

<application
	android:requestLegacyExternalStorage="true"

 

package com.example.toast;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import java.io.FileInputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

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

        Button btn = (Button)findViewById(R.id.btn);
        final EditText editText = (EditText)findViewById(R.id.editText);

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

        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                try{
                    FileInputStream inFs = new FileInputStream("/sdcard/sd_test.txt");

                    byte[] txt = new byte[inFs.available()];
                    inFs.read(txt);
                    editText.setText(new String(txt));
                    inFs.close();

                }catch(IOException e){
                    e.printStackTrace();
                    Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}

android 6.0부터는 보안 모델이 강화되어 ActivityCompat.requctPermission()를 이용해 각 앱마다 파일 등에 엑세스 할 수 있도록 코딩해야한다. 이 메소드를 사용하면 아래의 사진처럼 사용자가 거부/허용 할 수 있는 대화상자가 나오고 허용을 클랙해야만 파일 등에 접근이 가능하다.

 

이걸 코딩할 때 원래 AVD 11을 썼었는데 11에서는 이렇게 해도 접근 권한이 안되는 것이다 ㅠㅠ

그래서 AVD를 10으로 바꿨더니 정상적으로 작동했다.

 

sd카드에 디렉터리를 생성/삭제/읽기

 

<AndroidMainfest.xml>

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.file">

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

    <application
        android:requestLegacyExternalStorage="true"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.File">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

<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:orientation="vertical"
    android:padding="10dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn1"
        android:text="SD 카드에 디렉터리 생성"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn2"
        android:text="SD 카드에 디렉터리 삭제"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn3"
        android:text="시스템 폴더/파일 등록"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/editText"/>

</LinearLayout>

<MainActivity.java>

package com.example.file;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {
    Button btn1, btn2, btn3;
    EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ActivityCompat.requestPermissions(this, new String[]{
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        }, MODE_PRIVATE);

        btn1 = (Button)findViewById(R.id.btn1);
        btn2 = (Button)findViewById(R.id.btn2);
        btn3 = (Button)findViewById(R.id.btn3);
        editText = (EditText)findViewById(R.id.editText);

        final String strDpatch = Environment.getExternalStorageDirectory().getAbsolutePath();
        final File myDir = new File(strDpatch + "/mydir");

        btn1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v)
            {
                try{
                    myDir.mkdir();
                    Toast.makeText(getApplicationContext(), myDir.getAbsolutePath(), Toast.LENGTH_SHORT).show();
                }catch(Exception e){
                    e.printStackTrace();
                    Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();
                }
            }
        });

        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v)
            {
                try{
                    myDir.delete();
                    Toast.makeText(getApplicationContext(), myDir.getAbsolutePath(), Toast.LENGTH_LONG).show();
                } catch (Exception e){
                    Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_SHORT).show();
                }
            }
        });

        btn3.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                editText.setText(null);
                String sysDir = Environment.getRootDirectory().getAbsolutePath();
                File[] sysFiles = (new File(sysDir).listFiles());

                File[] sdFiles = (new File(strDpatch).listFiles());

                String strFname;
                for(int i=0; i<sdFiles.length; i++){
                    if(sdFiles[i].isDirectory() == true)
                        strFname = "<폴더> " + sdFiles[i].toString();
                    else
                        strFname = "<파일> " + sdFiles[i].toString();
                    editText.setText(editText.getText() + "\n" + strFname);
                }
            }
        });
    }
}

sd 카드 디렉터리가 잘 생성됐나 확인하려고 코드를 조금 변경했는데 만약 시스템 폴더를 확인하고 싶으면 sysFiles 변수로 뽑으면 된다. 시스템 디렉토리는 RootDirectory의 위치를 찾으면 된다.

 

 

'모바일' 카테고리의 다른 글

09-1. 그래픽  (0) 2021.02.23
08-2. SD 카드 폴더/파일 처리 연습  (0) 2021.02.22
07-3. 메뉴/토스트/다이얼로그 연습  (0) 2021.02.20
07-2. 토스트 & 대화상자  (0) 2021.02.20
07-1. 메뉴  (0) 2021.02.20
Comments