Java JSch меняет кодировку файла при копировании

Я передаю некоторые файлы с SFTP-сервера на другую машину. Файлы на SFTP находятся в кодировке UTF-8, мне нужны они в «cp1251», это способ изменить кодировку файлов во время копирования?

Я делаю это, как показано ниже:

ChannelSftp sftp_channel = (ChannelSftp) channel;
// some code
sftp_channel.get(src, dst);

person Daria    schedule 12.01.2015    source источник


Ответы (2)


Нет, ты не можешь этого сделать. Вы можете передавать только байты.

Что вам нужно сделать, это загрузить файл во временный файл, а затем преобразовать его:

final Path transferFile = Files.createTempFile("transfer", "xxx");

// transfer file to transferFile

// Sure it's not 1252?
final Charset srcCharset = Charset.forName("windows-1251");

final Path dstFile = Paths.get("destinationFile");
final Charset dstCharset = StandardCharsets.UTF_8;

final char[] buf = new char[16384]; // or another size
int charsRead;

try (
    final BufferedReader reader = Files.newBufferedReader(transferFile, srcCharset);
    final BufferedWriter writer = Files.newBufferedWriter(dstFile, dstCharset);
) {
    while ((charsRead = reader.read(buf)) != -1)
        writer.write(buf, 0, charsRead);
    writer.flush();
}

Files.delete(transferFile);

Теперь, если вы можете получить InputStream из файла, который хотите передать, код будет выглядеть следующим образом (ПРИМЕЧАНИЕ: закрытие исходного InputStream здесь НЕ обрабатывается; однако обратите внимание, что .close() Closeable является идемпотентным, поэтому даже если читатель закроет поток , вы все равно хотите его закрыть):

// "in" is the InputStream from the remote file
final Charset srcCharset = Charset.forName("windows-1251");

final Path dstFile = Paths.get("destinationFile");
final Charset dstCharset = StandardCharsets.UTF_8;

try (
    final FileSystem sshfs = xxx;
    final Reader reader = new InputStreamReader(in, srcCharset);
    final BufferedWriter writer  = Files.newBufferedWriter(dstFile, dstCharset);
) {
    while ((charsRead = reader.read(buf)) != -1)
        writer.write(buf, 0, charsRead);
    writer.flush();
}

ИДЕАЛЬНО: существует реализация JSR 203 через SFTP, однако я ее не нашел; если вы найдете его, вы можете даже использовать Files.copy():

final Charset srcCharset = Charset.forName("windows-1251");

final Path dstFile = Paths.get("destinationFile");
final Charset dstCharset = StandardCharsets.UTF_8;

try (
    final FileSystem sshfs = xxx;
    final BufferedReader reader 
        = Files.newBufferedReader(sshfs.getPath("sourcepath"), srcCharset);
    final BufferedWriter writer  = Files.newBufferedWriter(dstFile, dstCharset);
) {
    while ((charsRead = reader.read(buf)) != -1)
        writer.write(buf, 0, charsRead);
    writer.flush();
}
person fge    schedule 12.01.2015
comment
Спасибо за отзыв... Но некоторые файлы очень большие (более 1 Гб), не сложно ли перекодировать файлы? - person Daria; 12.01.2015
comment
А с чего бы это? Нет никаких причин для этого вообще ;) Но если у вас есть такие большие файлы, вам, вероятно, следует выбрать размер буфера больше 2048. - person fge; 12.01.2015
comment
Обратите внимание, что я серьезно отношусь к реализации JSR 203; если он существует, вам будет намного легче. Предполагается, что он существует, к сожалению, я не смог его найти ... Я попытаюсь обновить ответ, если найду его. - person fge; 12.01.2015
comment
Обратите внимание, что вопрос касается загрузки файла, а не загрузки. Таким образом, вы должны сделать преобразование после переноса, а не до. - person Martin Prikryl; 12.01.2015
comment
@MartinPrikryl ах, да, это совсем другое. Однако сделать это на лету будет совсем не просто. - person fge; 12.01.2015
comment
@fge Почему? Ваш код тоже может работать с потоками, не так ли? Я включил несколько ссылок на свой ответ. - person Martin Prikryl; 12.01.2015
comment
@MartinPrikryl эм, нет, у вас есть некоторые трудности, с которыми вы не ожидаете; например, вы читаете массив байтов, и не повезло, из трех байтов, необходимых для преобразования в char, у вас есть только два в конце;) - person fge; 12.01.2015
comment
Я понимаю эту проблему. Но чем отличается преобразование существующего локального файла (как в вашем коде)? Во всяком случае, я ожидаю, что читатель скроет это (вы читаете символы, а не байты). - person Martin Prikryl; 12.01.2015
comment
@MartinPrikryl, это тоже правда; вы могли бы использовать InputStreamReader. - person fge; 12.01.2015
comment
Большое спасибо за такие хорошие ответы ... теперь я собираюсь добиться наилучшей реализации всего, что я получил от вас. - person Daria; 12.01.2015
comment
@Steve.B, лучше всего было бы получить InputStream из удаленного файла; если да, используйте InputStreamReader с правильной кодировкой, затем откройте BufferedWriter в файле назначения и выполните передачу. - person fge; 12.01.2015

JSch не может преобразовать кодировку загруженного файла для вас. Вы должны сделать это самостоятельно, как показывает ответ @fge.

Чтобы не сохранять временную копию загруженного файла (в исходной кодировке), используйте сигнатуру метода get, возвращающего поток:

public InputStream get(String src)

Затем прочитайте непосредственно из этого потока и преобразуйте загруженные данные на лету, прежде чем фактически сохранять их в целевой файл.

Для преобразования «на лету» при чтении из потока см., например:

person Martin Prikryl    schedule 12.01.2015
comment
Вы уверены, что String в качестве аргумента - это то, что вы имели в виду? - person fge; 12.01.2015
comment
да. String в качестве пути к удаленному файлу для загрузки. И вы получаете InputStream для чтения. Я что-то пропустил? - person Martin Prikryl; 12.01.2015
comment
Ах, хорошо, я почему-то думал, что этот аргумент будет содержимым удаленного файла :/ - person fge; 12.01.2015