Файл читается на Windows, но не на Linux контейнере? - PullRequest
3 голосов
/ 06 августа 2020

Как сказано в заголовке, я не могу прочитать содержимое файла (файл csv) при выполнении того же кода в контейнере linux

private Set<VehicleConfiguration> loadConfigurations(Path file, CodeType codeType) throws IOException {

    log.debug("File exists? " + Files.exists(file));
    log.debug("Path " + file.toString());
    log.debug("File " + file.toFile().toString());
    log.debug("File absolute path " + file.toAbsolutePath().toString());

    String line;
    Set<VehicleConfiguration> configurations = new HashSet<>(); // this way we ignore duplicates in the same file
    try(BufferedReader br = new BufferedReader(new FileReader(file.toFile()))){
        while ((line = br.readLine()) != null)   {
            configurations.add(build(line, codeType));
        }
    }


    log.debug("Loaded " + configurations.size() + " configurations");
    return configurations;
}

Журналы возвращают "true" и путь к файлу в обеих системах (локально в windows и в linux docker контейнере). На windows он загружает «15185 конфигураций», но в контейнер загружает «0 конфигураций».

Файл существует на linux, я использую bash и проверяю его сам. Я использую команду head, и в файле есть строки.

До этого я пробовал использовать Files.lines следующим образом:

var vehicleConfigurations = Files.lines(file)
            .map(line -> build(line, codeType))
            .collect(Collectors.toCollection(HashSet::new));

Но у этого есть проблема (только для контейнера) с содержимым . Он читает файл, но не весь файл, он достигает заданной строки (скажем, строки 8000) и не читает ее полностью (читает примерно половину строки перед разделителем запятой). Затем я получаю java .lang.ArrayIndexOutOfBoundsException, потому что мой метод сборки пытается разделить эту строку, и я получаю доступ к индексу 1 (которого у него нет, только 0):

private VehicleConfiguration build(String line, CodeType codeType) {
    String[] cells = line.split(lineSeparator);
    var vc = new VehicleConfiguration();
    vc.setVin(cells[0]);
    vc.setCode(cells[1]);
    vc.setType(codeType);
    return vc;
}

Что может быть проблема? Я не понимаю, как тот же код (в Java) работает с Windows, но не с контейнером Linux. В этом нет никакого смысла.

Я использую Java 11. Файл копируется с использованием томов в docker -компонентном файле, например:

    volumes:
  - ./file-sources:/file-sources

Затем я копирую file (используя команду cp в контейнере linux) из источников файлов в / root, потому что именно там приложение ожидает прибытия новых файлов. Затем содержимое файла читается описанными мною методами. Пример данных файла (без странных символов):

содержимое файла

Заранее спасибо.

ОБНОВЛЕНИЕ: попробовано с помощью метода newBufferedReader, результат тот же (работает с windows, не работает с linux контейнером):

  private Set<VehicleConfiguration> loadConfigurations(Path file, CodeType codeType) throws IOException {
    String line;
    Set<VehicleConfiguration> configurations = new HashSet<>(); // this way we ignore duplicates in the same file
    try(BufferedReader br = Files.newBufferedReader(file)){
        while ((line = br.readLine()) != null)   {
            configurations.add(build(line, codeType));
        }
    }

    log.debug("Loaded " + configurations.size() + " configurations");
    return configurations;
}

w c -l в контейнере linux (in / root) возвращает: 15185 hard_001.csv

Обновление: это не решение, но я обнаружил, что, отбросив файлы прямо в папку источников файлов и сделайте эту папку папкой, которую прослушивает код, файлы читаются. В общем, кажется, что проблема более очевидна при использовании cp / mv внутри контейнера для другой папки. Может быть, файл читается до того, как он будет полностью скопирован / перемещен, и поэтому он читает 0 конфигураций?

1 Ответ

5 голосов
/ 06 августа 2020

В java есть несколько методов, которые вы никогда не должны использовать. когда-либо.

new FileReader(File) - один из них.

Каждый раз, когда у вас есть вещь, которая представляет байты, и как-то выпадают символы или строки, или наоборот? Никогда не используйте их, если в spe c указанного метода явно не указано, что он всегда использует предустановленную кодировку. Почти все такие методы используют «системную кодировку по умолчанию», что означает, что операция зависит от машины, на которой вы ее запускаете. Это сокращение от «это не удастся, и ваши тесты этого не поймут». Что вам не нужно.

Вот почему вы никогда не должны использовать эти вещи.

FileReader был исправлен (есть второй конструктор, который принимает кодировку), но это только с JDK11 . У вас уже есть красивый новый API, почему вы снова переключаетесь на изящный старый File API? Не делайте этого.

Все различные методы в Files, такие как Files.newBufferedReader, предназначены для использования UTF-8, если вы не укажете (в этом случае Files более полезен, и в отличие от большинство других java основных библиотек). Таким образом:

try (BufferedReader br = Files.newBufferedReader(file)) {

что просто .. лучше .., чем ваша линия.

Теперь, вероятно, это все равно не сработает. Но это хорошо! Это также выйдет из строя на вашей машине разработчика. Скорее всего, файл, который вы читаете, на самом деле не в UTF_8. Это вероятное предположение; большинство linuxen развертывается с кодировкой по умолчанию UTF_8, а большинство машин разработки - нет; если ваша машина разработчика работает, а ваша среда развертывания - нет, очевидный вывод состоит в том, что ваш входной файл не UTF_8. Это не обязательно должно быть то, что на вашем компьютере разработчика установлено по умолчанию; что-то вроде ISO_8859_1 никогда не будет генерировать исключения, но вместо этого будет читать gobbledygook. Ваш код может показаться работающим (без сбоев), но текст, который вы читаете, по-прежнему неверен.

Выясните, какая у вас кодировка текста, а затем укажите ее. Если это ISO_8859_1, например:

try (BufferedReader br = Files.newBufferedReader(file, StandardCharsets.ISO_8859_1)) {

, и теперь ваш код больше не имеет типа «работает на одних машинах, но не на других».

Проверьте строку, в которой произошел сбой, в шестнадцатеричный редактор, если нужно. Готов поспорить, доллары на пончики, там будет байт 0x80 или больше (в десятичном формате 128 или больше). Все, вплоть до 127, имеет тенденцию означать одно и то же в самых разных кодировках текста, от ASCII до любого варианта ISO-8859, до UTF-8 Windows Cp1252, до макромана и многих других вещей, при условии, что это все только простые буквы и цифры, неправильная кодировка не имеет никакого значения. Но как только вы дойдете до 0x80 или выше, все они станут другими. Вооруженный этим байтом + некоторое знание того, каким символом он должен быть, обычно является хорошим началом для выяснения, в какой кодировке находится этот текстовый файл.

NB: Если это не так, проверьте, как текст файл копируется с вашего компьютера разработчика в среду развертывания. Вы уверены, что это тот же файл? Если он копируется с помощью текстового механизма, снова может быть виновата кодировка кодировки, но на этот раз в том, как файл записан, а не в том, как ваше java приложение его читает.

...