:scrambleup: Tenho feito umas cópias de uns arquivos meio grandes ultimamente usando io como me foi sugerido aqui no GUJ… In/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?
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);
: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…)
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.
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?
:scrambleup: Eu tentei esse seu código e ganhei de presente uma java.io.IOException: Acesso negado
at sun.nio.ch.FileChannelImpl.truncate0(Native Method)
at sun.nio.ch.FileChannelImpl.map(Unknown Source)
at sun.nio.ch.FileChannelImpl.transferFromFileChannel(Unknown Source)
at sun.nio.ch.FileChannelImpl.transferFrom(Unknown Source)
at br.inf.empresaqualquer.update.util.FileHandler.copyFile(FileHandler.java:70) Alguma idéia do que faltou eu fazer?
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.