Nio: como copiar arquivos?

:scrambleup: Tenho feito umas cópias de uns arquivos meio grandes ultimamente usando io como me foi sugerido aqui no GUJIn/OutputStreams… Mas isso é muito lento! Me sugeriram aqui também (nem lembro onde ou quando) usar nio (New I/O). Ok, quero muito acatar essa sugestão, mas não sei como… Olhei no site da Sun, na documentação, mas isso ainda não me clareou as idéias… Portanto, como eu faria um

[code]fileIn = new FileInputStream(originalFile);
fileOut = new FileOutputStream(toFile);

int ch;

while ((ch = pmFileIn.read()) != -1){
fileOut.write(ch);
}
fileIn.close();
fileOut.close();
[/code]usando NIO? Tem jeito? Como? Posso usar meus InputStreams do pacote io como entrada?

Olá

  1. Estude melhor a documentação do java.io. Há meios mais rápidos de que copiar arquivos caracter por caracter. Aliás não use chars para copiar arquivos.

  2. Com java.nio se pode melhorar o desempenho desde que use buffers e todas as boas práticas.

  3. No ConexãoJava haverá uma palestra sobre java.nio e aqui no GUJ há um artigo sobre java.nio com um exemplo exatamente igual ao que quer fazer.

[]s
Luca

Acredite se quiser, mas copiar 1 arquivo com java.nio é mais facil que com java.io!

FileChannel from = new RandomAccessFile("from.txt", "r").getChannel();
FileChannel to= new RandomAccessFile("to.txt", "w").getChannel();
from.transferTo(0, from.size(), to);

Aqui tem ótimos exemplos acho que você vai gostar
http://www.javaalmanac.com/egs/java.nio/File2File.html

:scrambleup: Ok, vi os exemplos (tanto do GUJ quanto do Java Develpers Almanac) e os entendi. Beleza… Mas… E se eu quisesse interromper uma operação de cópia de arquivos? Na velha I/O bastava que eu substituísse while ((ch = pmFileIn.read()) != -1){ fileOut.write(ch); } por while (((ch = pmFileIn.read()) != -1) && !stop){ fileOut.write(ch); } com stop sendo um boolean que eu pudesse alterar a qualquer momento… Como eu faço isso com New I/O? O que eu faço com o código [code]try {
// Create channel on the source
FileChannel srcChannel = new FileInputStream("srcFilename").getChannel();

    // Create channel on the destination
    FileChannel dstChannel = new FileOutputStream("dstFilename").getChannel();

    // Copy file contents from source to destination
    dstChannel.transferFrom(srcChannel, 0, srcChannel.size());

    // Close the channels
    srcChannel.close();
    dstChannel.close();
} catch (IOException e) {
}[/code] para que ele pare a cópia num comando do usuário? Ou eu devo usar outro paradigma de cópias com [b]nio[/b] para poder parar a cópia qaundo quiser?

Ler a documentação ajuda viu! :x
A responsta ta na descrição do método!

Thread t = Thread.currentThread();
...
try {
   from.transferTo(0, to.size(), to);
} catch(ClosedByInterruptException e) {
    //thread for interrompida! paramos aqui
}

...
em outra thread simplesmente fazemos:
t.interrupt();

Ou ainda podemos simplesmente fechar um dos channels.
Vale lembrar que transfer[To/From] quando interrompidos não tem como saber quantos bytes foram transferidos com sucesso.

[quote=“louds”]Ler a documentação ajuda viu! :x
A responsta ta na descrição do método!

Ou ainda podemos simplesmente fechar um dos channels.
Vale lembrar que transfer[To/From] quando interrompidos não tem como saber quantos bytes foram transferidos com sucesso.[/quote]

:scrambleup: louds, my friend, eu li a documentação…

:agrue:

O lance é que com o interrupt() eu não tenho certeza de como, quando ou se o serviço vai parar… Tentei também fechar um dos canais, mas aparentemente não funcionou… Se eu mando fechar um canal, parece, sei lá, que ele tá bloqueado e a chamada close() parece esperar até o final da cópia pra ocorrer (mesmo numa Thread separada…)

Mas, realmente, minha primeira idéia era o interrupt(), como você pode ver em http://www.guj.com.br/forum/viewtopic.php?t=14331… Mas não rolou… Tem alguma dica, velho?

Ps.: a documentação de Java é muito boa e tal, mas nem sempre a Sun tem as respostas pra todas as nossas perguntas… :wink:

Se você está usando Windows, FileChannel.transferFrom ou .transferTo são implementados via mapeamento de arquivos em memória.
(Se você adora ler fontes em C, e tem paciência suficiente para baixar o fonte COMPLETO do JDK 1.4, baixe a versão SCSL do código-fonte do JDK, e veja os arquivos j2sesrcwindows
ativejavasun
iochFileChannelImpl.c e srcshareclassessun
iochFileChannelImpl.java).
A transferência é feita de modo tal que não pode ser interrompida, porque é como se fosse um “System.arraycopy”. Você consegue interromper um “System.arraycopy”?
Se você quer copiar as coisas rápido (e põe rápido nisso; é mais rápido que em C++, a menos que você use APIs do Windows), use transferFrom ou transferTo.
Se você quer controle, use o velho e bom FileInputStream/FileOutputStream e aposte uma corrida com o Rubinho Barrichello…

:scrambleup: Caramba! Não sabia disso… E ainda fiquei com raiva do meu código… :lol: Bom, agora que sei disso, vou procurar, sei lá, impedir apenas que a cópia do próximo arquivo comece quando o usuário cancelar uma operação dessas… Mas, é verdade, a diferença entre os tipos de cópia (FileChannel New I/O e FileStreams I/O convencional) é monstruosa… Estou usando um computador com um processador de 2.4GHz e a cópia com “old” I/O chegou a um pico de 71% de uso do processador, enquanto a outra (New I/O), além de rápida, exigiu menos da máquina. Mas tudo tem seu preço… Desempenho custa controle. Pessoalmente, fico com desempenho…

:arrow: :?: Curiosidade… Em outros sistemas que não o Window$, a cópia via FileChannel também é impossível de ser parada?

:arrow: :!: Tenho uma outra dúvida sobre FileChannel que é sobre monitorar progresso com ele… Desculpem por estar fazendo isso, mas, preciso muito de uma :idea: e até agora não tive nenhuma resposta em 2 dias… Essa dúvida de monitorar progresso está em http://www.guj.com.br/forum/viewtopic.php?t=14391. Novamente, desculpem linkar esse tópico a outro desta forma, mas existe um fator desespero…

martui, desculpe por ter sido meio grosso.
Porem nunca precisei explicitamente interromper 1 copia via transferXXX.
Uma solução que eu vejo é voce implementar copia usando buffers/nio de forma análoga a feita com streams.
thingol, eu imaginei que em win32 eles usassem a API de filecopy, mas vai lá saber qual a performance dela…

:scrambleup: Cara, não esquente a cabeça não… Nem pensei nisso…

Como seria isso? Será que você poderia me indicar um exemplo? E… Mais uma coisa: quanto a performance… Usar buffers/nio seria tão eficiente quanto usar FileChannel? Qual seria uma comparação (em termos de tempo) entre usar Streams “old” I/O, FileChannel e buffers/nio?

Em Linux:
transferTo chama a API sendfile. Como quase todas as APIs do Unix podem ser interrompidas (errno = EINTR), acho que dá para interromper sem problemas, mas isso é o caso de testar.

Em Solaris:
transferTo chama a API sendfilev, carregada dinamicamente de /usr/lib/libsendfile.so.1. Fora isso, é igual ao Linux.

Estou falando baseado apenas no código fonte da 1.4.2 do JDK da Sun, fonte j2se/src/solaris/native/sun/nio/ch/FileChannelImpl.c, função Java_sun_nio_ch_FileChannelImpl_transferTo0().

Não sei se o JDK da IBM, ou da BEA, implementam FileChannel.transferTo dessa maneira.

Droga, apertei o botão errado … o botão de criar nova mensagem deveria ser mais escondido que o botão de reply.

É mais fácil fazer o benchmark do que falar, mas estou chutando que teríamos a seguinte situação:

Tempo gasto:
FileChannel.transferTo < NIO Buffers < BufferedXStream+FileXStream < FileXStream puro

Consumo de memória:
FileChannel.transferTo < FileXStream < BufferedXStream + FileXStream < NIO Bufffers

Consumo de CPU (supondo que, se você tiver um disco ATA, que esteja configurado para usar DMA para reduzir o consumo de CPU em I/O):
FileChannel.transferTo < NIO Buffers < BufferedXStream + FileXStream < FileXStream

É estranho que eu esteja chutando que BufferedXStream + FileXStream consuma menos CPU que FileXStream sozinho (afinal, está executando mais código), mas é que BufferedXStream evita que FileXStream esteja constantemente acessando o sistema operacional via JNI, o que consome um monte de CPU.

Obviamente é necessário fazer o teste, porque na prática aparecem coisas que não são esperadas só pela teoria. (Para explicar os resultados, é preciso mudar a teoria…)

:scrambleup: Entendo… Se não for pedir demais… Digamos que o FileChannel fosse assim, uma Ferrari e os clássicos FileXXputStream fossem as Minardis… Os Buffers/nio seriam o que?

E mais uma coisa:

Como eu faço isso? Nunca fiz e nem vi exemplos ainda… Onde tem exemplos sobre isso? Alguém poderia fornecê-los?

Isso copia usando um buffer.

ByteBuffer bb = ByteBuffer.allocateDirect&#40;2048&#41;;
FileChannel from = ...
FileChannel to = ...
bb.clear&#40;&#41;;
while&#40;from.read&#40;bb&#41; != -1&#41; &#123;
  bb.flip&#40;&#41;;
  to.write&#40;bb&#41;;
  bb.clear&#40;&#41;;
&#125;

:scrambleup: Eu tentei esse seu código e ganhei de presente uma java.io.IOException&#58; Acesso negado at sun.nio.ch.FileChannelImpl.truncate0&#40;Native Method&#41; at sun.nio.ch.FileChannelImpl.map&#40;Unknown Source&#41; at sun.nio.ch.FileChannelImpl.transferFromFileChannel&#40;Unknown Source&#41; at sun.nio.ch.FileChannelImpl.transferFrom&#40;Unknown Source&#41; at br.inf.empresaqualquer.update.util.FileHandler.copyFile&#40;FileHandler.java&#58;70&#41; Alguma idéia do que faltou eu fazer?

:arrow: Estou usando Window$.

:scrambleup: Deculpa, pessoal, resolvi o problema… Bastava comentar uma linha que tinha ficado de um código antigo que estava ali… Foi mal… :roll:

Até agora, o esquema com o ByteBuffer foi bem rapidinho, quase se equiparando ao FileChannel puro. Não dá pra saber quem é mais rápido…

Olá!

Como estás a fazer cópia de arquivos, poderás aumentar o tamanho do buffer um pouco, que deve melhorar o desempenho.
Se quiser, faça testes com 8192 bytes ou 16384 bytes de buffer.