[Resolvido] nio Files.size não retorna tamanho correto
7 respostas
juno.rr
Olá pessoal.
Criei uma classe para calcular o tamanho total dos arquivos de um diretório, utilizando java.nio.file.Files#walkFileTree(…).
O que acontece é que o método Files.size( Path ) retorna sempre o mesmo resultado para qualquer arquivo, sempre 4096 bytes.
Li que a implementação depende do sistema operacional e do sistema de arquivos e o resultado dependo do arquivo. Neste caso são arquivos de banco de dados access e arquivos de textos, mas para qualquer um o resultado é o tamanho do bloco do sistema de arquivos e não o tamanho do arquivo em si. Alguém sabe o porque disto e como contornar?
Bom, deixa eu escrever um método desses para testar.
Mas imagino que você esteja chamando Files.size sobre um diretório; como você deve saber, um diretório também é um arquivo (se você estiver usando Unix/Linux, você consegue saber até o tamanho do arquivo que armazena as entradas do diretório. Esse arquivo começa com o tamanho igual ao de um cluster, e depois vai subindo - se você criar um diretório com muitas entradas e depois deletar todas, provavelmente o tamanho não se alterará.
No caso do Windows, esse tamanho não é reportado para a aplicação e aparece como zero (mesmo para uma aplicaçao Cygwin).
juno.rr
Não é sobre um diretório, é sobre os arquivos mesmo. Vou colocar o código:
publicclassSizeFileVisitorimplementsFileVisitor<Path>{privatePathpath;privatelongsize;publicSizeFileVisitor(Stringpath){if(path==null||path.trim().isEmpty())thrownewIllegalArgumentException("Invalid path: "+path);size=0;this.path=Paths.get(path);if(!Files.exists(this.path))thrownewIllegalArgumentException("path does not exists: "+path);}publicSizeFileVisitor(Pathpath){if(path==null||!Files.exists(path))thrownewIllegalArgumentException("Invalid path: "+path);size=0;this.path=path;}publiclongcalculateSize(){size=0;try{Files.walkFileTree(path,this);returnsize;}catch(IOExceptionex){thrownewRuntimeException(ex);}}@OverridepublicFileVisitResultpreVisitDirectory(Pathdir,BasicFileAttributesattrs)throwsIOException{returnFileVisitResult.CONTINUE;}@OverridepublicFileVisitResultvisitFile(Pathfile,BasicFileAttributesattrs)throwsIOException{size+=Files.size(file);returnFileVisitResult.CONTINUE;}@OverridepublicFileVisitResultvisitFileFailed(Pathfile,IOExceptionexc)throwsIOException{returnFileVisitResult.TERMINATE;}@OverridepublicFileVisitResultpostVisitDirectory(Pathdir,IOExceptionexc)throwsIOException{returnFileVisitResult.CONTINUE;}publicstaticvoidmain(String[]args){SizeFileVisitorsf=newSizeFileVisitor("D:/zzz/src");System.out.println("* calculating size (D:/zzz/src) ...");System.out.println("* size = "+sf.calculateSize());}}
Como da pra ver na linha 41, o Files.size( Path ) é chamado para cada arquivo que é encontrado.
gomesrod
Colei sua classe aqui exatamente igual e funcionou.
Só fiz uma alteração que foi colocar um print no início do método visitFile()
E ele mostrou o tamanho de cada arquivo em bytes, conferi alguns pelas propriedades do arquivo no Windows e está certinho.
O total do diretório também bate com o que o Windows informa.
E
entanglement
Rodei o programa abaixo em uma máquina Windows. De fato, para alguns diretórios, o tamanho reportado é zero, para outros é 4096, 8192 etc.
De qualquer forma, esse valor é na prática quase inútil para você. Ele é o “tamanho do diretório” mas não é o tamanho correto, já que para diretórios com algumas poucas entradas ele reporta como zero em vez de 4096.
packageguj;importjava.util.*;importjava.io.*;importjava.nio.file.*;importjava.nio.file.attribute.*;publicclassListarDiretorioComJavaNioFile{/** * @param args */publicstaticvoidmain(String[]args)throwsException{Pathstart=FileSystems.getDefault().getPath(args[0]);Set<FileVisitOption>options=newTreeSet<FileVisitOption>();Files.walkFileTree(start,options,Integer.MAX_VALUE,newSimpleFileVisitor<Path>(){@OverridepublicFileVisitResultvisitFile(Pathfile,BasicFileAttributesattrs)throwsIOException{// O tamanho de um arquivo é realmente o tamanho de um arquivoassert(attrs.size()==Files.size(file));returnFileVisitResult.CONTINUE;}@OverridepublicFileVisitResultpostVisitDirectory(Pathdir,IOExceptione)throwsIOException{if(e==null){// Files.size() para um diretório, no caso do Windows, é uma estimativa inferior// do tamanho do arquivo que representa o diretório, // não dos arquivos contidos dentro desse diretórioSystem.out.printf("%,7d : %s%n",Files.size(dir),dir.toString());returnFileVisitResult.CONTINUE;}else{e.printStackTrace();returnFileVisitResult.CONTINUE;}}});}}
E
entanglement
Bom, então vamos lá. O jeito mais bobo, então, de achar uma soma recursiva dos tamanhos dos arquivos do diretório e subdiretórios, vai abaixo.
Na prática, provavelmente você injetaria algum “listener” para poder atualizar, por exemplo, um JLabel, para que a espera não seja excessiva por parte do usuário.
Note que eu usei “attrs.size()” em vez de “Files.size (file)” embora em tese devesse dar o mesmo resultado.
Acredito que possam dar resultados diferentes se você não tiver acesso ao arquivo, apenas ao diretório (isso não é incomum no Unix, talvez seja possível fazer no Windows com algum esforço).
juno.rr
Legal pessoal, valeu pela força. entanglement, atualizei meu código para como está o seu e funcionou, mas não faz muito sentido isso…
Pensei em uma teoria: O diretório que estava fazendo o teste era o drive D:, uma partição separada formatada em ntfs. Havia colado aquela pasta (D:\zzz\src) para testes. Depois de alterar o código, funcionou. Acredito q o windows não havia gravado os dados de fato ainda, por isso retornava sempre 4096 para qualquer arquivo.
Será q isso faz sentido?