читати файл з активів


178
public class Utils {
    public static List<Message> getMessages() {
        //File file = new File("file:///android_asset/helloworld.txt");
        AssetManager assetManager = getAssets();
        InputStream ims = assetManager.open("helloworld.txt");    
     }
}

Я використовую цей код, намагаючись прочитати файл із активів. Я спробував два способи зробити це. По-перше, при використанні Fileя отримав FileNotFoundException, коли використання AssetManager getAssets()методу не розпізнається. Чи є тут якесь рішення?

Відповіді:


225

Ось, що я роблю в ході роботи з буферного читання, розширюю чи змінюю, щоб відповідати вашим потребам

BufferedReader reader = null;
try {
    reader = new BufferedReader(
        new InputStreamReader(getAssets().open("filename.txt")));

    // do reading, usually loop until end of file reading  
    String mLine;
    while ((mLine = reader.readLine()) != null) {
       //process line
       ...
    }
} catch (IOException e) {
    //log the exception
} finally {
    if (reader != null) {
         try {
             reader.close();
         } catch (IOException e) {
             //log the exception
         }
    }
}

EDIT: Моя відповідь, можливо, марна, якщо ваше питання стосується того, як це зробити поза діяльністю. Якщо ваше питання полягає лише в тому, як прочитати файл з ресурсу, відповідь вище.

ОНОВЛЕННЯ :

Щоб відкрити файл із зазначенням типу, просто додайте тип у виклику InputStreamReader, як описано нижче.

BufferedReader reader = null;
try {
    reader = new BufferedReader(
        new InputStreamReader(getAssets().open("filename.txt"), "UTF-8")); 

    // do reading, usually loop until end of file reading 
    String mLine;
    while ((mLine = reader.readLine()) != null) {
       //process line
       ...
    }
} catch (IOException e) {
    //log the exception
} finally {
    if (reader != null) {
         try {
             reader.close();
         } catch (IOException e) {
             //log the exception
         }
    }
}

EDIT

Як говорить @Stan у коментарі, код, який я даю, - це не підсумовування рядків. mLineзамінюється кожен пропуск. Ось чому я і писав //process line. Я припускаю, що файл містить якісь дані (тобто список контактів), і кожен рядок повинен оброблятися окремо.

Якщо ви просто хочете завантажити файл без будь-якої обробки, вам доведеться підводити підсумки mLineпри кожному пропуску за допомогою StringBuilder()та додавання кожного проходу.

ІНШЕ РЕДАКТУВАННЯ

Відповідно до коментаря @Vincent я додав finallyблок.

Також зауважте, що в Java 7 і версії ви можете використовувати try-with-resourcesдля використання AutoCloseableіCloseable особливості недавнього Java.

КОНТЕКСТ

У коментарі @LunarWatcher зазначає, що getAssets()це classв context. Отже, якщо ви називаєте його поза межами, activityвам потрібно звернутися до нього та передати контекстний екземпляр діяльності.

ContextInstance.getAssets();

Це пояснено у відповіді @Maneesh. Тож якщо вам це корисно, ви підтримуєте його відповідь, тому що саме він це вказав.


2
@Stan, потім напишіть про це в коментарях і дозвольте авторові вирішити, чи хотіли б оновити його. Редагування - це покращення ясності, а не зміна значення. Зміни коду завжди слід розміщувати як коментарі.
KyleMit

2
Ваш код не гарантує закриття потоку та своєчасне звільнення ресурсу. Я рекомендую вам використовувати finally {reader.close();}.
Вінсент Кантін

2
Я думаю, що корисно зазначити, що наведений вище код показує помилку в ADT - "reader.close ();" рядок потрібно помістити в інший блок пробного лову. Перевірте цю тему: stackoverflow.com/questions/8981589 / ... :)
JakeP

1
getAssets - це клас Context, тому для використання поза діяльністю потрібно зателефонувати на Context. Тож поза діяльністю це буде щось на зразокcontext.getAssets(.....)
Zoe

1
Згідно з вашим оновленням (спасибі за додавання цього btw), контекст у статичних полях - це витік пам'яті. Це слід застосовувати обережно і належним чином очистити. Інакше у вас виникла пам'ять, яка може мати великий вплив на додаток.
Зої

65
getAssets()

працює лише в розділі "Активність" в будь-якому іншому класі, який ви повинні використовувати Contextдля цього.

Зробіть конструктор класу Utils для передачі посилань на діяльність (некрасивий спосіб) або контекст програми як параметр для нього. Використовуючи це, використовуйте getAsset () у вашому класі Utils.


1
Він працює над усім, що є підкласом контексту, діяльність якого є однією з багатьох.
Джеремі Логан

Щойно зазначив, що написав Context.
user370305

@ user370305 Ви знаєте, як я можу перетворити InputStream у FileInputStream?
hotHead

@ user370305 Яким чином це некрасиво?
Севастьян Саванюк

Передача об'єктів інтерфейсу користувача, не враховуючи наслідків, є поганою практикою. Якщо ви не будете обережні, ви можете викликати витоки пам'яті або спробувати використовувати мертві контексти. Добре читайте на тему: android.jlelse.eu/memory-leak-patterns-in-android-4741a7fcb570
milosmns

49

Краще пізно, ніж ніколи.

У мене виникли труднощі з читанням файлів по черзі за певними обставинами. Наведений нижче метод - найкращий, який я знайшов, до цього часу, і рекомендую його.

Використання: String yourData = LoadData("YourDataFile.txt");

Якщо передбачається, що YourDataFile.txt знаходиться в активах /

 public String LoadData(String inFile) {
        String tContents = "";

    try {
        InputStream stream = getAssets().open(inFile);

        int size = stream.available();
        byte[] buffer = new byte[size];
        stream.read(buffer);
        stream.close();
        tContents = new String(buffer);
    } catch (IOException e) {
        // Handle exceptions here
    }

    return tContents;

 }

Мій рядок повернення - android.content.res.AssetManager$AssetInputStream@4195dfa0 ..
Boldijar Paul

те саме, res.AssetManager $ AssetInputStream @ .... якась конкретна причина, чому він повертає це?
Біг

Ви подвійно розподіляєте пам'ять - спочатку для буфера, а потім для String. Не працює для великих файлів.
JaakL

1
ідеальний спосіб виділити розмір буфера stream.available().
Казим Рангвала

39
public String ReadFromfile(String fileName, Context context) {
    StringBuilder returnString = new StringBuilder();
    InputStream fIn = null;
    InputStreamReader isr = null;
    BufferedReader input = null;
    try {
        fIn = context.getResources().getAssets()
                .open(fileName, Context.MODE_WORLD_READABLE);
        isr = new InputStreamReader(fIn);
        input = new BufferedReader(isr);
        String line = "";
        while ((line = input.readLine()) != null) {
            returnString.append(line);
        }
    } catch (Exception e) {
        e.getMessage();
    } finally {
        try {
            if (isr != null)
                isr.close();
            if (fIn != null)
                fIn.close();
            if (input != null)
                input.close();
        } catch (Exception e2) {
            e2.getMessage();
        }
    }
    return returnString.toString();
}

Ви можете подумати, що якщо ви закриєте BufferedReader, він повинен автоматично також закривати InputStreanReader і InputStream. Тому що те, що ви не створюєте для цих ручок, наприклад input = new BufferedReader(new InputStreamReader(fIn));.
перехід

3
Я б запропонував створити окремі блоки "try / catch" для закриття всіх ваших ресурсів наприкінці; а не згуртовування їх усіх в одне - так як це може залишити інші ресурси незакритими, якщо попередня спроба закрити інший ресурс викидає виняток.
Рис

9
AssetManager assetManager = getAssets();
InputStream inputStream = null;
try {
    inputStream = assetManager.open("helloworld.txt");
}
catch (IOException e){
    Log.e("message: ",e.getMessage());
}

8

одне рядкове рішення для kotlin:

fun readFileText(fileName: String): String {
    return assets.open(fileName).bufferedReader().use { it.readText() }
}

7

getAssets() метод буде працювати, коли ви телефонуєте всередині класу Діяльність.

Якщо ви називаєте цей метод у класі не активності, вам потрібно викликати цей метод із контексту, який передається з класу активності. Отже нижче наводиться рядок, за яким ви можете отримати доступ до методу.

ContextInstance.getAssets();

ContextInstance може бути передано як клас діяльності.


5

Читання та запис файлів завжди було багатослівним та схильним до помилок. Уникайте цих відповідей і просто використовуйте Okio :

public void readLines(File file) throws IOException {
  try (BufferedSource source = Okio.buffer(Okio.source(file))) {
    for (String line; (line = source.readUtf8Line()) != null; ) {
      if (line.contains("square")) {
        System.out.println(line);
      }
    }
  }
}

1
Чи знаєте ви, чому це виглядає більш естетично і коротко? Ну, тому що ви тут пропустили, принаймні, половину коду. Опущені частини: 1) IOExceptionблок "спробу / захоплення" 2) Закриття потоків у разі викиду винятків 3) Цей код читає один рядок, а не весь файл. Ця функціональна бібліотека, безумовно, є такою в своєму роді, в цьому немає жодних сумнівів. А тепер скажіть, чи варто все-таки уникати "цих відповідей" і застосовувати Okio лише для читання файлів? Відповідь "НІ", якщо це вже не є вашим додатком.
Фарид

Оновлено мою відповідь.
Сакет

4

Ось спосіб зчитування файлу в ресурсах:

/**
 * Reads the text of an asset. Should not be run on the UI thread.
 * 
 * @param mgr
 *            The {@link AssetManager} obtained via {@link Context#getAssets()}
 * @param path
 *            The path to the asset.
 * @return The plain text of the asset
 */
public static String readAsset(AssetManager mgr, String path) {
    String contents = "";
    InputStream is = null;
    BufferedReader reader = null;
    try {
        is = mgr.open(path);
        reader = new BufferedReader(new InputStreamReader(is));
        contents = reader.readLine();
        String line = null;
        while ((line = reader.readLine()) != null) {
            contents += '\n' + line;
        }
    } catch (final Exception e) {
        e.printStackTrace();
    } finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException ignored) {
            }
        }
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException ignored) {
            }
        }
    }
    return contents;
}

Це хороша відповідь, але це поганий підхід до використання рядкового конкатенації. Спробуйте використовувати замість StringBuilder. StringBuilder contentBuilder = новий StringBuilder (); while ((line = reader.readLine ())! = null) {builder.append ("\ n"). append (line); } І наприкінці ви можете створити новий об'єкт String за цим: content = contentBuilder.toString ();
Бартеріо

4

Ви можете завантажити вміст з файлу. Вважайте, що файл присутній у папці ресурсів.

public static InputStream loadInputStreamFromAssetFile(Context context, String fileName){
    AssetManager am = context.getAssets();
    try {
        InputStream is = am.open(fileName);
        return is;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

public static String loadContentFromFile(Context context, String path){
    String content = null;
    try {
        InputStream is = loadInputStreamFromAssetFile(context, path);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();
        content = new String(buffer, "UTF-8");
    } catch (IOException ex) {
        ex.printStackTrace();
        return null;
    }
    return content;
}

Тепер ви можете отримати вміст, зателефонувавши до наступної функції

String json= FileUtil.loadContentFromFile(context, "data.json");

Враховуючи, що data.json зберігається в Application \ app \ src \ main \ properties \ data.json


3

В MainActivity.java

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

        TextView tvView = (TextView) findViewById(R.id.tvView);

        AssetsReader assetsReader = new AssetsReader(this);
        if(assetsReader.getTxtFile(your_file_title)) != null)
        {
            tvView.setText(assetsReader.getTxtFile(your_file_title)));
        }
    }

Також ви можете створити окремий клас, який виконує всю роботу

public class AssetsReader implements Readable{

    private static final String TAG = "AssetsReader";


    private AssetManager mAssetManager;
    private Activity mActivity;

    public AssetsReader(Activity activity) {
        this.mActivity = activity;
        mAssetManager = mActivity.getAssets();
    }

    @Override
    public String getTxtFile(String fileName)
    {
        BufferedReader reader = null;
        InputStream inputStream = null;
        StringBuilder builder = new StringBuilder();

        try{
            inputStream = mAssetManager.open(fileName);
            reader = new BufferedReader(new InputStreamReader(inputStream));

            String line;

            while((line = reader.readLine()) != null)
            {
                Log.i(TAG, line);
                builder.append(line);
                builder.append("\n");
            }
        } catch (IOException ioe){
            ioe.printStackTrace();
        } finally {

            if(inputStream != null)
            {
                try {
                    inputStream.close();
                } catch (IOException ioe){
                    ioe.printStackTrace();
                }
            }

            if(reader != null)
            {
                try {
                    reader.close();
                } catch (IOException ioe)
                {
                    ioe.printStackTrace();
                }
            }
        }
        Log.i(TAG, "builder.toString(): " + builder.toString());
        return builder.toString();
    }
}

На мою думку, краще створити інтерфейс, але це не потрібно

public interface Readable {
    /**
     * Reads txt file from assets
     * @param fileName
     * @return string
     */
    String getTxtFile(String fileName);
}

2

Якщо ви використовуєте інший будь-який клас, крім діяльності, вам, можливо, захочеться зробити таке,

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( YourApplication.getInstance().getAssets().open("text.txt"), "UTF-8"));

2

Використовуючи Kotlin, ви можете зробити наступне, щоб прочитати файл з активів в Android:

try {
    val inputStream:InputStream = assets.open("helloworld.txt")
    val inputString = inputStream.bufferedReader().use{it.readText()}
    Log.d(TAG,inputString)
} catch (e:Exception){
    Log.d(TAG, e.toString())
}

2

Можливо, пізно, але заради інших, хто шукає персикових відповідей:

public static String loadAssetFile(Context context, String fileName) {
    try {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(context.getAssets().open(fileName)));
        StringBuilder out= new StringBuilder();
        String eachline = bufferedReader.readLine();
        while (eachline != null) {
            out.append(eachline);
            eachline = bufferedReader.readLine();
        }
        return out.toString();
    } catch (IOException e) {
        Log.e("Load Asset File",e.toString());
    }
    return null;
}

1

cityfile.txt

   public void getCityStateFromLocal() {
        AssetManager am = getAssets();
        InputStream inputStream = null;
        try {
            inputStream = am.open("city_state.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
        ObjectMapper mapper = new ObjectMapper();
        Map<String, String[]> map = new HashMap<String, String[]>();
        try {
            map = mapper.readValue(getStringFromInputStream(inputStream), new TypeReference<Map<String, String[]>>() {
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        ConstantValues.arrayListStateName.clear();
        ConstantValues.arrayListCityByState.clear();
        if (map.size() > 0)
        {
            for (Map.Entry<String, String[]> e : map.entrySet()) {
                CityByState cityByState = new CityByState();
                String key = e.getKey();
                String[] value = e.getValue();
                ArrayList<String> s = new ArrayList<String>(Arrays.asList(value));
                ConstantValues.arrayListStateName.add(key);
                s.add(0,"Select City");
                cityByState.addValue(s);
                ConstantValues.arrayListCityByState.add(cityByState);
            }
        }
        ConstantValues.arrayListStateName.add(0,"Select States");
    }
 // Convert InputStream to String
    public String getStringFromInputStream(InputStream is) {
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        String line;
        try {
            br = new BufferedReader(new InputStreamReader(is));
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return sb + "";

    }

Тепер посилання повертаєтьсяFile no longer available That file has now been permanently removed and cannot be recovered
gregn3

1

Клас Сканер може спростити це.

        StringBuilder sb=new StringBuilder();
        Scanner scanner=null;
        try {
            scanner=new Scanner(getAssets().open("text.txt"));
            while(scanner.hasNextLine()){
                sb.append(scanner.nextLine());
                sb.append('\n');
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(scanner!=null){try{scanner.close();}catch (Exception e){}}
        }
        mTextView.setText(sb.toString());

Ви можете об'єднати ці 2 рядки sb.append (scanner.nextLine ()); sb.append ('\ n'); to sb.appendln (scanner.nextLine ());
Мойтаба

0

@HpTerm відповідь версії Котліна :

private fun getDataFromAssets(): String? {

    var bufferedReader: BufferedReader? = null
    var data: String? = null

    try {
        bufferedReader = BufferedReader(
            InputStreamReader(
                activity?.assets?.open("Your_FILE.html"),     
                "UTF-8"
            )
        )                  //use assets? directly if in activity

       var mLine:String = bufferedReader?.readLine()
        while (mLine != null) {
            data+= mLine
            mLine=bufferedReader.readLine()
        }

    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        try {
            bufferedReader?.close()
        } catch (e: Exception) {
           e.printStackTrace()
        }
    }
    return data
}

Наразі це додасть нуль до рядка. Необхідні зміни: var mLine:Stringповинні бути var mLine:String? var data: String?повинні бути var data = ""повертається тип повинен бутиString
fupduck

0

Ось спосіб , щоб отримати InputStreamдля файлу в assetsпапці без Context, Activity, Fragmentабо Application. Як ви отримуєте дані з цього, InputStreamзалежить від вас. Тут є багато пропозицій щодо цього в інших відповідях.

Котлін

val inputStream = ClassLoader::class.java.classLoader?.getResourceAsStream("assets/your_file.ext")

Java

InputStream inputStream = ClassLoader.class.getClassLoader().getResourceAsStream("assets/your_file.ext");

Усі ставки знімаються, якщо ClassLoaderв грі є звичай

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.