รับขนาดของโฟลเดอร์หรือไฟล์


106

ฉันจะดึงขนาดของโฟลเดอร์หรือไฟล์ใน Java ได้อย่างไร?


เหมือนกัน แต่เน้นประสิทธิภาพ: stackoverflow.com/questions/116574/…
Ciro Santilli 郝海东郝海东冠状六四事件法轮功

หากคุณเกิดขึ้นจะต้องอยู่บนAndroidStatFsนั้นจะดูที่ ใช้สถิติระบบไฟล์และเร็วกว่าวิธีการเรียกซ้ำเกือบ 1,000 เท่าซึ่งใช้ไม่ได้กับความต้องการของเรา การใช้งานของเราสามารถพบได้ที่นี่: stackoverflow.com/a/58418639/293280
Joshua Pinter

คำตอบ:


192
java.io.File file = new java.io.File("myfile.txt");
file.length();

สิ่งนี้จะคืนค่าความยาวของไฟล์เป็นไบต์หรือ0หากไม่มีไฟล์ ไม่มีวิธีในตัวในการรับขนาดของโฟลเดอร์คุณจะต้องเดินแผนผังไดเรกทอรีซ้ำ ๆ (โดยใช้listFiles()วิธีการของวัตถุไฟล์ที่แสดงถึงไดเร็กทอรี) และสะสมขนาดไดเร็กทอรีด้วยตัวคุณเอง:

public static long folderSize(File directory) {
    long length = 0;
    for (File file : directory.listFiles()) {
        if (file.isFile())
            length += file.length();
        else
            length += folderSize(file);
    }
    return length;
}

คำเตือน : วิธีนี้ไม่แข็งแรงเพียงพอสำหรับการใช้งานจริง directory.listFiles()อาจจะกลับมาและสาเหตุnull NullPointerExceptionนอกจากนี้ยังไม่พิจารณา symlink และอาจมีโหมดความล้มเหลวอื่น ๆ ใช้วิธีนี้ .


11
ระวังถ้าคุณเรียกใช้สิ่งนี้ในไดเร็กทอรี C: root บนเครื่อง Windows มีไฟล์ระบบอยู่ซึ่ง (อ้างอิงจาก java.io.File) ทั้งไฟล์หรือไดเร็กทอรี คุณอาจต้องการเปลี่ยน else-clause เพื่อตรวจสอบว่า File นั้นเป็นไดเร็กทอรีจริงๆ
Paul Clapham

2
การเปลี่ยนแปลงง่ายๆในการตรวจสอบพารามิเตอร์เพื่อดูว่าไม่ใช่ไดเร็กทอรีที่จุดเริ่มต้นของวิธีการหรือไม่และส่งคืนความยาวจากนั้นการเรียกซ้ำจะง่ายกว่า - เพียงเพิ่มการเรียกตัวเองด้วยวิธีการเดียวกันจากนั้นจึงรองรับการส่งผ่านในการอ้างอิงไฟล์แทน ของไดเร็กทอรีด้วย
Kevin Brock

3
หากคุณใช้ Java 7 หรือสูงกว่าให้ใช้คำตอบstackoverflow.com/a/19877372/40064จะเร็วกว่ามาก
Wim Deblauwe

1
สิ่งนี้จะสับสนโดย symlinks นอกจากนี้อาจทำให้เกิดNullPointerExceptionกรณีที่มีการแก้ไขไดเร็กทอรีพร้อมกัน
Aleksandr Dubinsky

43

การใช้ java-7 nio api การคำนวณขนาดโฟลเดอร์สามารถทำได้เร็วกว่ามาก

นี่คือตัวอย่างพร้อมใช้งานที่มีประสิทธิภาพและจะไม่เกิดข้อยกเว้น จะบันทึกไดเรกทอรีที่ไม่สามารถเข้าหรือมีปัญหาในการข้ามผ่าน Symlinks จะถูกละเว้นและการแก้ไขไดเร็กทอรีพร้อมกันจะไม่ทำให้เกิดปัญหามากเกินความจำเป็น

/**
 * Attempts to calculate the size of a file or directory.
 * 
 * <p>
 * Since the operation is non-atomic, the returned value may be inaccurate.
 * However, this method is quick and does its best.
 */
public static long size(Path path) {

    final AtomicLong size = new AtomicLong(0);

    try {
        Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {

                size.addAndGet(attrs.size());
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) {

                System.out.println("skipped: " + file + " (" + exc + ")");
                // Skip folders that can't be traversed
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) {

                if (exc != null)
                    System.out.println("had trouble traversing: " + dir + " (" + exc + ")");
                // Ignore errors traversing a folder
                return FileVisitResult.CONTINUE;
            }
        });
    } catch (IOException e) {
        throw new AssertionError("walkFileTree will not throw IOException if the FileVisitor does not");
    }

    return size.get();
}

สิ่งนี้เทียบเท่ากับการพัฒนา Android หรือไม่?
นักพัฒนา Android

มีเหตุผลในการใช้AtomicLongแทน just longหรือไม่?
Lukas Schmelzeisen

ตัวแปรจะต้องเป็นขั้นสุดท้ายเมื่อเข้าถึงจากคลาสที่ไม่ระบุชื่อ
Aksel Willgert

1
ฉันทำการเปรียบเทียบโดยใช้ JMH และวิธี NIO api นี้เร็วกว่าประมาณ 4 ถึง 5 เท่าเมื่อเทียบกับรหัส commons-io (ทดสอบกับโฟลเดอร์ที่มีโฟลเดอร์ย่อยจำนวนมากรวม 180229 ไฟล์) Commons IO ใช้เวลา 15 วินาที NIO ใช้เวลา 5 วินาที
Wim Deblauwe

3
แนวทางนี้มีประสิทธิภาพมากที่สุด มันเกี่ยวข้องกับ symlinks, การปรับเปลี่ยนพร้อมกัน, ข้อยกเว้นด้านความปลอดภัย, ใช้ได้กับทั้งไฟล์และไดเร็กทอรี ฯลฯ มันแย่เกินไปที่Filesไม่รองรับโดยตรง!
Aleksandr Dubinsky

38

คุณต้องFileUtils#sizeOfDirectory(File)จากคอมมอน-io

โปรดทราบว่าคุณจะต้องตรวจสอบด้วยตนเองว่าไฟล์นั้นเป็นไดเร็กทอรีหรือไม่เนื่องจากเมธอดจะแสดงข้อยกเว้นหากมีการส่งผ่านที่ไม่ใช่ไดเร็กทอรีไป

คำเตือน : วิธีนี้ (ณ commons-io 2.4) มีข้อผิดพลาดและอาจเกิดข้อผิดพลาดIllegalArgumentExceptionหากมีการแก้ไขไดเร็กทอรีพร้อมกัน


จะเกิดอะไรขึ้นเมื่อไฟล์ไม่ใช่ไดเร็กทอรี เมื่อมันไม่มี? ฯลฯ - เอกสารแย่แค่ไหน
Mr_and_Mrs_D

@Mr_and_Mrs_D - เพียงแค่คัดลอกและวางบรรทัดหลังcheckDirectory(directory);เครื่องหมายถูก ตรวจสอบให้แน่ใจว่าFile.listFilesมีบุตร Refs: FileUtils.sizeOfDirectory () , File.listFiles ()
Mr.Polywhirl

3
ดูข้อผิดพลาดIO-449 วิธีนี้จะIllegalArgumentExceptionทำให้ไดเร็กทอรีถูกแก้ไขในขณะที่กำลังทำซ้ำ
Aleksandr Dubinsky

อุ๊ย !!! มันห่วย ... ใช่มันแสดงรายการไฟล์แล้วถ้าไฟล์หนึ่งถูกลบในขณะที่โค้ดกำลังทำงานอยู่มันก็จะพ่นออกมา
Dean Hiller

19

ใน Java 8:

long size = Files.walk(path).mapToLong( p -> p.toFile().length() ).sum();

มันจะดีกว่าที่จะใช้Files::sizeในขั้นตอนแผนที่ แต่มันมีข้อยกเว้นที่ตรวจสอบแล้ว

อัปเดต:
คุณควรทราบด้วยว่าสิ่งนี้สามารถทำให้เกิดข้อยกเว้นได้หากไม่สามารถเข้าถึงไฟล์ / โฟลเดอร์บางส่วนได้ เห็นนี้คำถามและแก้ปัญหาอื่นโดยใช้ฝรั่ง


1
ฉันกำลังมองหาสิ่งที่คล้ายกันและลงเอยด้วยรหัสที่เป็นปัญหา: stackoverflow.com/questions/22867286/…ตามที่คุณเห็นมีอีกแง่มุมหนึ่งของการจัดการข้อผิดพลาดที่ทำให้เกิดปัญหาเช่นกัน
Aksel Willgert

@AkselWillgert ขอบคุณนี่เป็นเรื่องโชคร้ายและฉันได้อัปเดตคำตอบแล้ว ตอนนี้เปลี่ยนเป็น Guava stackoverflow.com/a/24757556/1180621
Andrejs

10
public static long getFolderSize(File dir) {
    long size = 0;
    for (File file : dir.listFiles()) {
        if (file.isFile()) {
            System.out.println(file.getName() + " " + file.length());
            size += file.length();
        }
        else
            size += getFolderSize(file);
    }
    return size;
}

1
@ แสดงรหัสของคุณต้องมีการแก้ไขง่ายๆในการเรียกซ้ำคุณควรเพิ่มขนาดเป็นขนาดที่มีอยู่ไม่ใช่แค่กำหนดให้ size += getFolderSize(file);
เตชะกันต์จำเนียร

@Teja: ขอบคุณที่ชี้ให้เห็น แต่การเปลี่ยนแปลงจะอยู่ในคำสั่ง if เช่นกัน
Vishal

บางครั้งในโฟลเดอร์ที่กำลังเติบโต (เธรดอื่นกำลังดาวน์โหลดไฟล์และโฟลเดอร์และในเวลาเดียวกันฉันกำลังพิมพ์ขนาดโฟลเดอร์ที่กำลังดำเนินการอยู่) จะให้ค่าnullpointerexceptionที่บรรทัด "for (File file: dir.listFiles ()) {" ไฟล์บางไฟล์ปรากฏขึ้นและหายไปอย่างรวดเร็วในโฟลเดอร์ที่มีชีวิต ดังนั้นฉันจึงเพิ่มการตรวจสอบค่าว่างสำหรับค่าส่งคืนdir.listFiles ()ก่อนหน้าสำหรับลูป
csonuryilmaz

จาก File.listFiles () javadoc: "อาร์เรย์จะว่างเปล่าหากไดเร็กทอรีว่างส่งกลับค่า nullหากชื่อพา ธ นามธรรมนี้ไม่ได้แสดงถึงไดเร็กทอรีหรือหากเกิดข้อผิดพลาด I / O" ดังนั้นความคิดเห็นข้างต้นจึงมีประโยชน์เมื่อรับขนาดโฟลเดอร์ในโฟลเดอร์ที่เปลี่ยนแปลงแบบไดนามิก
csonuryilmaz

7

สำหรับJava 8นี่เป็นวิธีที่ถูกต้องวิธีหนึ่ง:

Files.walk(new File("D:/temp").toPath())
                .map(f -> f.toFile())
                .filter(f -> f.isFile())
                .mapToLong(f -> f.length()).sum()

การกรองไดเรกทอรีทั้งหมดเป็นสิ่งสำคัญเนื่องจากวิธีการความยาวไม่รับประกันว่าจะเป็น 0 สำหรับไดเรกทอรี

อย่างน้อยรหัสนี้ก็ให้ข้อมูลขนาดเดียวกันกับที่ Windows Explorer ทำ


5

นี่คือวิธีที่ดีที่สุดในการรับขนาดไฟล์ทั่วไป (ใช้ได้กับไดเร็กทอรีและไม่ใช่ไดเร็กทอรี):

public static long getSize(File file) {
    long size;
    if (file.isDirectory()) {
        size = 0;
        for (File child : file.listFiles()) {
            size += getSize(child);
        }
    } else {
        size = file.length();
    }
    return size;
}

แก้ไข: โปรดทราบว่าการดำเนินการนี้อาจใช้เวลานาน อย่ารันบนเธรด UI

นอกจากนี้ที่นี่ (นำมาจากhttps://stackoverflow.com/a/5599842/1696171 ) เป็นวิธีที่ดีในการรับสตริงที่ผู้ใช้อ่านได้จากการส่งคืนแบบยาว:

public static String getReadableSize(long size) {
    if(size <= 0) return "0";
    final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
    int digitGroups = (int) (Math.log10(size)/Math.log10(1024));
    return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups))
            + " " + units[digitGroups];
}

4

หากคุณต้องการใช้Java 8 NIO API โปรแกรมต่อไปนี้จะพิมพ์ขนาดเป็นไบต์ของไดเร็กทอรีที่อยู่

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class PathSize {

    public static void main(String[] args) {
        Path path = Paths.get(".");
        long size = calculateSize(path);
        System.out.println(size);
    }

    /**
     * Returns the size, in bytes, of the specified <tt>path</tt>. If the given
     * path is a regular file, trivially its size is returned. Else the path is
     * a directory and its contents are recursively explored, returning the
     * total sum of all files within the directory.
     * <p>
     * If an I/O exception occurs, it is suppressed within this method and
     * <tt>0</tt> is returned as the size of the specified <tt>path</tt>.
     * 
     * @param path path whose size is to be returned
     * @return size of the specified path
     */
    public static long calculateSize(Path path) {
        try {
            if (Files.isRegularFile(path)) {
                return Files.size(path);
            }

            return Files.list(path).mapToLong(PathSize::calculateSize).sum();
        } catch (IOException e) {
            return 0L;
        }
    }

}

calculateSizeวิธีการที่เป็นสากลสำหรับPathวัตถุดังนั้นจึงยังสามารถใช้ได้กับไฟล์ หมายเหตุว่าถ้าแฟ้มหรือไดเรกทอรีไม่สามารถเข้าถึงได้ในกรณีนี้กลับขนาด0ของวัตถุเส้นทางที่จะเป็น


3

File.length()( Javadoc ).

โปรดทราบว่าสิ่งนี้ใช้ไม่ได้กับไดเรกทอรีหรือไม่รับประกันว่าจะใช้งานได้

สำหรับไดเร็กทอรีคุณต้องการอะไร หากเป็นขนาดรวมของไฟล์ทั้งหมดที่อยู่ข้างใต้คุณสามารถเดินวนซ้ำ ๆ โดยใช้File.list()และรวมขนาดของไฟล์เหล่านั้นFile.isDirectory()ได้



3
  • ใช้งานได้กับAndroidและJava
  • ใช้ได้กับทั้งโฟลเดอร์และไฟล์
  • ตรวจสอบตัวชี้ว่างทุกที่ที่จำเป็น
  • ละเว้นลิงก์สัญลักษณ์หรือทางลัด
  • พร้อมผลิต!

รหัสแหล่งที่มา:

   public long fileSize(File root) {
        if(root == null){
            return 0;
        }
        if(root.isFile()){
            return root.length();
        }
        try {
            if(isSymlink(root)){
                return 0;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return 0;
        }

        long length = 0;
        File[] files = root.listFiles();
        if(files == null){
            return 0;
        }
        for (File file : files) {
            length += fileSize(file);
        }

        return length;
    }

    private static boolean isSymlink(File file) throws IOException {
        File canon;
        if (file.getParent() == null) {
            canon = file;
        } else {
            File canonDir = file.getParentFile().getCanonicalFile();
            canon = new File(canonDir, file.getName());
        }
        return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
    }

1

สำหรับ windows การใช้ java.io ฟังก์ชันเรียกซ้ำนี้มีประโยชน์

    public static long folderSize(File directory) {
    long length = 0;

    if (directory.isFile())
         length += directory.length();
    else{
        for (File file : directory.listFiles()) {
             if (file.isFile())
                 length += file.length();
             else
                 length += folderSize(file);
        }
    }

    return length;
}

นี่ได้รับการทดสอบและทำงานอย่างถูกต้องในตอนท้ายของฉัน


1

ฉันทดสอบdu -c <folderpath>แล้วและเร็วกว่า nio 2 เท่าไฟล์หรือการเรียกซ้ำ

private static long getFolderSize(File folder){
  if (folder != null && folder.exists() && folder.canRead()){
    try {
      Process p = new ProcessBuilder("du","-c",folder.getAbsolutePath()).start();
      BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
      String total = "";
      for (String line; null != (line = r.readLine());)
        total = line;
      r.close();
      p.waitFor();
      if (total.length() > 0 && total.endsWith("total"))
        return Long.parseLong(total.split("\\s+")[0]) * 1024;
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
  return -1;
}

0
public long folderSize (String directory)
    {
        File curDir = new File(directory);
        long length = 0;
        for(File f : curDir.listFiles())
        {
            if(f.isDirectory())
            {               
                 for ( File child : f.listFiles()) 
                 {
                     length = length + child.length();
                 }

                System.out.println("Directory: " + f.getName() + " " + length + "kb");
            }
            else
            {
                length = f.length();
                System.out.println("File: " + f.getName() + " " + length + "kb");
            }
            length = 0;
        }
        return length;
    }

0

หลังจากการค้นคว้าและค้นหาโซลูชันต่างๆมากมายที่เสนอที่ StackOverflow ในที่สุดฉันก็ตัดสินใจเขียนวิธีแก้ปัญหาของตัวเอง จุดประสงค์ของฉันคือไม่มีกลไกการโยนเพราะฉันไม่ต้องการผิดพลาดหาก API ไม่สามารถดึงขนาดโฟลเดอร์ได้ วิธีนี้ไม่เหมาะสำหรับสถานการณ์แบบมัลติเธรด

ก่อนอื่นฉันต้องการตรวจสอบไดเร็กทอรีที่ถูกต้องในขณะที่ข้ามโครงสร้างระบบไฟล์

private static boolean isValidDir(File dir){
    if (dir != null && dir.exists() && dir.isDirectory()){
        return true;
    }else{
        return false;
    }
}

ประการที่สองฉันไม่ต้องการให้การเรียกซ้ำของฉันไปที่ symlinks (softlinks) และรวมขนาดไว้ในผลรวมทั้งหมด

public static boolean isSymlink(File file) throws IOException {
    File canon;
    if (file.getParent() == null) {
        canon = file;
    } else {
        canon = new File(file.getParentFile().getCanonicalFile(),
                file.getName());
    }
    return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
}

ในที่สุดการใช้งานแบบเรียกซ้ำของฉันเพื่อดึงขนาดของไดเร็กทอรีที่ระบุ สังเกตการตรวจสอบค่าว่างสำหรับ dir.listFiles () ตามที่ javadoc มีความเป็นไปได้ที่วิธีนี้สามารถคืนค่าว่างได้

public static long getDirSize(File dir){
    if (!isValidDir(dir))
        return 0L;
    File[] files = dir.listFiles();
    //Guard for null pointer exception on files
    if (files == null){
        return 0L;
    }else{
        long size = 0L;
        for(File file : files){
            if (file.isFile()){
                size += file.length();
            }else{
                try{
                    if (!isSymlink(file)) size += getDirSize(file);
                }catch (IOException ioe){
                    //digest exception
                }
            }
        }
        return size;
    }
}

ครีมบางอย่างบนเค้ก API เพื่อรับขนาดของไฟล์รายการ (อาจเป็นไฟล์และโฟลเดอร์ทั้งหมดที่อยู่ในรูท)

public static long getDirSize(List<File> files){
    long size = 0L;
    for(File file : files){
        if (file.isDirectory()){
            size += getDirSize(file);
        } else {
            size += file.length();
        }
    }
    return size;
}


0

คุณสามารถใช้Apache Commons IOเพื่อค้นหาขนาดโฟลเดอร์ได้อย่างง่ายดาย

หากคุณใช้ maven โปรดเพิ่มการอ้างอิงต่อไปนี้ในpom.xmlไฟล์ของคุณ

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

หากไม่ใช่แฟนของ Maven ให้ดาวน์โหลด jar ต่อไปนี้และเพิ่มลงในเส้นทางชั้นเรียน

https://repo1.maven.org/maven2/commons-io/commons-io/2.6/commons-io-2.6.jar

public long getFolderSize() {

    File folder = new File("src/test/resources");
    long size = FileUtils.sizeOfDirectory(folder);

    return size; // in bytes
}

ในการรับขนาดไฟล์ผ่าน Commons IO

File file = new File("ADD YOUR PATH TO FILE");

long fileSize = FileUtils.sizeOf(file);

System.out.println(fileSize); // bytes

นอกจากนี้ยังสามารถทำได้ผ่านทาง Google Guava

สำหรับ Maven ให้เพิ่มสิ่งต่อไปนี้:

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.1-jre</version>
</dependency>

หากไม่ได้ใช้ Maven ให้เพิ่มสิ่งต่อไปนี้ในพา ธ คลาส

https://repo1.maven.org/maven2/com/google/guava/guava/28.1-jre/guava-28.1-jre.jar

public long getFolderSizeViaGuava() {
        File folder = new File("src/test/resources");
        Iterable<File> files = Files.fileTreeTraverser()
                .breadthFirstTraversal(folder);
        long size = StreamSupport.stream(files.spliterator(), false)
                .filter(f -> f.isFile())
                .mapToLong(File::length).sum();

        return  size;
    }

ในการรับขนาดไฟล์

 File file = new File("PATH TO YOUR FILE");
 long s  = file.length();
 System.out.println(s);

0
private static long getFolderSize(Path folder) {
        try {
            return Files.walk(folder)
                      .filter(p -> p.toFile().isFile())
                      .mapToLong(p -> p.toFile().length())
                      .sum();
        } catch (IOException e) {
            e.printStackTrace();
            return 0L;
        }

แม้ว่ารหัสของคุณจะดูดี แต่ฉันไม่แน่ใจว่ามันเพิ่มอะไรให้กับคำตอบอื่น ๆ หากเป็นเช่นนั้นโปรดแก้ไขคำตอบของคุณเพื่ออธิบาย
Dragonthoughts

เป็นเพียงเวอร์ชันปรับปรุงของการทำงานเดียวกันโดยใช้โค้ดน้อยลง
Jaskaran Singh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.