đọc tệp từ tài sản


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");    
     }
}

Tôi đang sử dụng mã này để cố gắng đọc một tệp từ tài sản. Tôi đã thử hai cách để làm điều này. Đầu tiên, khi sử dụng Filetôi nhận được FileNotFoundException, khi sử dụng AssetManager getAssets()phương thức không được công nhận. Có giải pháp nào ở đây không?

Câu trả lời:


225

Dưới đây là những gì tôi làm trong một hoạt động để đọc / mở rộng bộ đệm để phù hợp với nhu cầu của bạn

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: Câu trả lời của tôi có lẽ là vô ích nếu câu hỏi của bạn là làm thế nào để thực hiện nó bên ngoài một hoạt động. Nếu câu hỏi của bạn chỉ đơn giản là làm thế nào để đọc một tệp từ tài sản thì câu trả lời là ở trên.

CẬP NHẬT :

Để mở tệp chỉ định loại, chỉ cần thêm loại trong lệnh gọi InputStreamReader như sau.

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
         }
    }
}

BIÊN TẬP

Như @Stan nói trong bình luận, mã tôi đang đưa ra không phải là tóm tắt các dòng. mLineđược thay thế mỗi lần vượt qua. Đó là lý do tại sao tôi viết //process line. Tôi giả sử tệp chứa một số loại dữ liệu (ví dụ: danh sách liên hệ) và mỗi dòng nên được xử lý riêng.

Trong trường hợp bạn chỉ muốn tải tệp mà không cần bất kỳ loại xử lý nào, bạn sẽ phải tổng hợp mLinetại mỗi lượt sử dụng StringBuilder()và nối thêm mỗi lượt.

EDIT KHÁC

Theo nhận xét của @Vincent tôi đã thêm finallykhối.

Cũng lưu ý rằng trong Java 7 trở lên, bạn có thể sử dụng try-with-resourcesđể sử dụng các tính năng AutoCloseableCloseabletính năng của Java gần đây.

BỐI CẢNH

Trong một bình luận @LunarWatcher chỉ ra rằng đó getAssets()là mộtclass trong context. Vì vậy, nếu bạn gọi nó bên ngoài một activitybạn cần tham khảo nó và chuyển thể hiện ngữ cảnh cho hoạt động.

ContextInstance.getAssets();

Điều này được giải thích trong câu trả lời của @Maneesh. Vì vậy, nếu điều này hữu ích cho bạn, hãy nêu lên câu trả lời của anh ấy bởi vì đó là người đã chỉ ra điều đó.


2
@Stan, sau đó viết về nó trong các bình luận và để tác giả quyết định xem họ có muốn cập nhật nó không. Chỉnh sửa là để cải thiện rõ ràng, không thay đổi ý nghĩa. Sửa đổi mã phải luôn luôn được đăng dưới dạng bình luận đầu tiên.
KyleMit

2
Mã của bạn không đảm bảo đóng luồng và giải phóng tài nguyên kịp thời. Tôi khuyên bạn nên sử dụng finally {reader.close();}.
Vincent Cantin

2
Tôi nghĩ thật hữu ích khi chỉ ra rằng đoạn mã trên hiển thị lỗi trong ADT - "reader.close ();" dòng cần phải được đặt trong một khối thử bắt khác. Kiểm tra chủ đề này: stackoverflow.com/questions/8981589/, :)
JakeP

1
getAssets là một lớp trong Ngữ cảnh, vì vậy để sử dụng bên ngoài hoạt động, một cuộc gọi đến Ngữ cảnh cần được thực hiện. Vì vậy, bên ngoài một hoạt động, nó sẽ giống nhưcontext.getAssets(.....)
Zoe

1
Theo cập nhật của bạn (cảm ơn vì đã thêm btw đó), có bối cảnh trong các trường tĩnh là rò rỉ bộ nhớ. Điều này nên được sử dụng một cách thận trọng và được làm sạch đúng cách. Nếu không, bạn sẽ bị rò rỉ bộ nhớ có thể ảnh hưởng lớn đến ứng dụng.
Zoe

65
getAssets()

chỉ hoạt động trong Activity trong bất kỳ lớp nào khác mà bạn phải sử dụng Contextcho nó.

Tạo một hàm tạo cho lớp Utils vượt qua tham chiếu hoạt động (cách xấu) hoặc bối cảnh của ứng dụng làm tham số cho nó. Sử dụng sử dụng getAsset () trong lớp Utils của bạn.


1
Nó hoạt động trên bất cứ thứ gì là một lớp con của Ngữ cảnh, trong đó Activity là một trong số nhiều.
Jeremy Logan

Chỉ cần lưu ý tôi đã viết Context.
user370305

@ user370305 bạn có biết, làm thế nào tôi có thể chuyển đổi InputStream thành FileInputStream?
hotHead 18/05/2015

@ user370305 theo cách nào thì xấu thế này?
Sevastyan Savanyuk

Vượt qua các đối tượng UI xung quanh mà không xem xét hậu quả nói chung là một thực tiễn xấu. Nếu bạn không cẩn thận, bạn có thể gây rò rỉ bộ nhớ hoặc cố gắng sử dụng bối cảnh chết. Đọc tốt về chủ đề: android.jlelse.eu/memory-leak-potypes-in-android-4741a7fcb570
milosmns 31/12/18

49

Muộn còn hơn không.

Tôi gặp khó khăn khi đọc các tập tin từng dòng trong một số trường hợp. Phương pháp dưới đây là tốt nhất tôi tìm thấy, cho đến nay, và tôi khuyên bạn nên dùng nó.

Sử dụng: String yourData = LoadData("YourDataFile.txt");

Nơi YourDataFile.txt được giả định cư trú trong tài sản /

 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;

 }

Chuỗi trả về của tôi là android.content.res.AssetManager$AssetInputStream@4195dfa0 ..
Boldijar Paul

tương tự ở đây, res.AssetManager $ AssetInputStream @ .... có lý do cụ thể nào khiến nó trả lại cái này không?
Lớn

Bạn phân bổ gấp đôi bộ nhớ - đầu tiên cho bộ đệm và sau đó cho Chuỗi. Không hoạt động cho các tập tin lớn hơn.
JaakL

1
cách hoàn hảo để phân bổ kích thước để đệm stream.available().
Kasim Rangwala

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();
}

Bạn sẽ nghĩ rằng nếu bạn đóng BufferedReader, thì nó sẽ phải tự động đóng InputStreanReader và InputStream. Bởi vì những gì bạn không tạo ra một xử lý cho những người, ví dụ input = new BufferedReader(new InputStreamReader(fIn));.
xuyên

3
Tôi sẽ đề nghị tạo các khối thử / bắt riêng biệt để đóng tất cả các tài nguyên của bạn vào cuối; thay vì gộp tất cả chúng thành một - vì nó có thể khiến các tài nguyên khác không được tiết lộ nếu một nỗ lực trước đó để đóng tài nguyên khác ném ngoại lệ.
lừa

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

8

giải pháp một dòng cho kotlin:

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

7

getAssets() phương thức sẽ hoạt động khi bạn gọi bên trong lớp Activity.

Nếu bạn gọi phương thức này trong lớp không hoạt động thì bạn cần gọi phương thức này từ Ngữ cảnh được truyền từ lớp Activity. Vì vậy, dưới đây là dòng bởi bạn có thể truy cập phương pháp.

ContextInstance.getAssets();

ContextInstance có thể được thông qua như thế này của lớp Activity.


5

Đọc và viết các tập tin luôn luôn dài dòng và dễ bị lỗi. Tránh những câu trả lời này và chỉ sử dụng Okio thay thế:

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
Bạn có biết tại sao điều này trông thẩm mỹ và ngắn hơn? Chà, bởi vì bạn đã bỏ qua, ít nhất, một nửa mã ở đây. Các phần bị bỏ qua: 1) IOExceptionkhối thử / bắt 2) Đóng luồng trong trường hợp ngoại lệ được ném 3) Mã này đọc một dòng duy nhất, không phải toàn bộ tệp. Hiệu suất-khôn ngoan, thư viện này chắc chắn là một trong những loại của nó, không có nghi ngờ về điều đó. Bây giờ, hãy nói cho tôi biết tôi vẫn nên tránh "những câu trả lời" này và triển khai Okio chỉ để đọc tệp? Câu trả lời là KHÔNG, trừ khi nó đã là một phần của ứng dụng của bạn.
Farid

Cập nhật câu trả lời của tôi.
Saket

4

Đây là một phương pháp để đọc một tệp trong tài sản:

/**
 * 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;
}

Đây là một câu trả lời tốt, nhưng nó là cách tiếp cận tồi để sử dụng nối chuỗi. Thay vào đó hãy xem xét sử dụng StringBuilder. StringBuilder contentBuilder = new StringBuilder (); while (line = reader.readLine ())! = null) {builder.append ("\ n"). append (line); } Và cuối cùng, bạn có thể tạo đối tượng String mới bằng cách này: content = contentBuilder.toString ();
Barterio

4

Bạn có thể tải nội dung từ tập tin. Xem xét các tập tin có mặt trong thư mục tài sản.

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;
}

Bây giờ bạn có thể lấy nội dung bằng cách gọi hàm như sau

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

Xem xét data.json được lưu trữ tại Application \ app \ src \ main \ nội dung \ data.json


3

Trong 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)));
        }
    }

Ngoài ra, bạn có thể tạo lớp riêng biệt thực hiện tất cả công việc

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();
    }
}

Theo tôi, tốt hơn là tạo một giao diện, nhưng nó không cần thiết

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

2

Nếu bạn sử dụng bất kỳ lớp nào khác ngoài Hoạt động, bạn có thể muốn làm như thế,

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

2

Sử dụng Kotlin, bạn có thể thực hiện các thao tác sau để đọc tệp từ tài sản trong 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

Có thể đã quá muộn nhưng vì lợi ích của những người khác đang tìm kiếm câu trả lời đào hoa:

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 + "";

    }

Liên kết bây giờ trở lạiFile no longer available That file has now been permanently removed and cannot be recovered
gregn3

1

Lớp Scanner có thể đơn giản hóa việc này.

        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());

Bạn có thể hợp nhất 2 dòng sb.append (Scanner.nextLine ()); sb.append ('\ n'); đến sb.appendln (máy quét.nextLine ());
Mojtaba

0

@HpTerm trả lời phiên bản Kotlin:

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
}

Hiện tại điều này sẽ trả trước null cho chuỗi. Thay đổi cần thiết: var mLine:Stringnên var mLine:String? var data: String?nên var data = ""kiểu trả về nênString
fupduck

0

Đây là một cách để có được một InputStreamcho một tập tin trong assetsthư mục mà không có một Context, Activity, Fragmenthoặc Application. Làm thế nào bạn có được dữ liệu từ đó InputStreamlà tùy thuộc vào bạn. Có rất nhiều gợi ý cho điều đó trong các câu trả lời khác ở đây.

Kotlin

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

Java

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

Tất cả các cược được tắt nếu một tùy chỉnh ClassLoaderđược chơi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.