【问题标题】:Split and join back a binary file in java在java中拆分并加入二进制文件
【发布时间】:2011-05-24 20:02:07
【问题描述】:

我正在尝试将二进制文件(如视频/音频/图像)分成 100kb 的块,然后将这些块重新连接起来以取回原始文件。 我的代码似乎正在工作,因为它划分文件并加入块,我返回的文件与原始文件大小相同。但是,问题是内容被截断 - 也就是说,如果是视频文件,它会在 2 秒后停止,如果是图像文件,则只有上部看起来正确。

这是我正在使用的代码(如果您愿意,我可以发布整个代码):

用于分割:

File ifile = new File(fname); 
FileInputStream fis;
String newName;
FileOutputStream chunk;
int fileSize = (int) ifile.length();
int nChunks = 0, read = 0, readLength = Chunk_Size;
byte[] byteChunk;
try {
    fis = new FileInputStream(ifile);
    StupidTest.size = (int)ifile.length();
    while (fileSize > 0) {
        if (fileSize <= Chunk_Size) {
            readLength = fileSize;
        }
        byteChunk = new byte[readLength];
        read = fis.read(byteChunk, 0, readLength);
        fileSize -= read;
        assert(read==byteChunk.length);
        nChunks++;
        newName = fname + ".part" + Integer.toString(nChunks - 1);
        chunk = new FileOutputStream(new File(newName));
        chunk.write(byteChunk);
        chunk.flush();
        chunk.close();
        byteChunk = null;
        chunk = null;
    }
    fis.close();
    fis = null;

对于加入文件,我将所有块的名称放在一个列表中,然后按名称对其进行排序,然后运行以下代码:

File ofile = new File(fname);
FileOutputStream fos;
FileInputStream fis;
byte[] fileBytes;
int bytesRead = 0;
try {
    fos = new FileOutputStream(ofile,true);             
    for (File file : files) {
        fis = new FileInputStream(file);
        fileBytes = new byte[(int) file.length()];
        bytesRead = fis.read(fileBytes, 0,(int)  file.length());
        assert(bytesRead == fileBytes.length);
        assert(bytesRead == (int) file.length());
        fos.write(fileBytes);
        fos.flush();
        fileBytes = null;
        fis.close();
        fis = null;
    }
    fos.close();
    fos = null;

【问题讨论】:

    标签: java file-io


    【解决方案1】:
    public class FileSplitter {
        private static final int BUFSIZE = 4*1024;
        public boolean needsSplitting(String file, int chunkSize) {
            return new File(file).length() > chunkSize;
        }
        private static boolean isASplitFileChunk(String file) {
            return chunkIndexLen(file) > 0;
        }
        private static int chunkIndexLen(String file) {
            int n = numberOfTrailingDigits(file);
            if (n > 0) {
                String zeroes = new String(new char[n]).replace("\0", "0");
                if (file.matches(".*\\.part[0-9]{"+n+"}?of[0-9]{"+n+"}?$") && !file.endsWith(zeroes) && !chunkNumberStr(file, n).equals(zeroes)) {
                    return n;
                }
            }
            return 0;
        }
        private static String getWholeFileName(String chunkName) {
            int n = chunkIndexLen(chunkName);
            if (n>0) {
                return chunkName.substring(0, chunkName.length() - 7 - 2*n); // 7+2n: 1+4+n+2+n : .part012of345
            }
            return chunkName;
        }
        private static int getNumberOfChunks(String filename) {
            int n = chunkIndexLen(filename);
            if (n > 0) {
                try {
                    String digits = chunksTotalStr(filename, n);
                    return Integer.parseInt(digits);
                } catch (NumberFormatException x) { // should never happen
                }
            }
            return 1;
        }
        private static int getChunkNumber(String filename) {
            int n = chunkIndexLen(filename);
            if (n > 0) {
                try {
                    // filename.part001of200
                    String digits = chunkNumberStr(filename, n);
                    return Integer.parseInt(digits)-1;
                } catch (NumberFormatException x) {
                }
            }
            return 0;
        }
        private static int numberOfTrailingDigits(String s) {
            int n=0, l=s.length()-1;
            while (l>=0 && Character.isDigit(s.charAt(l))) {
                n++; l--;
            }
            return n;
        }
        private static String chunksTotalStr(String filename, int chunkIndexLen) {
            return filename.substring(filename.length()-chunkIndexLen);
        }
        protected static String chunkNumberStr(String filename, int chunkIndexLen) {
            int p = filename.length() - 2 - 2*chunkIndexLen; // 123of456
            return filename.substring(p,p+chunkIndexLen);
        }
        // 0,8 ==> part1of8; 7,8 ==> part8of8
        private static String chunkFileName(String filename, int n, int total, int chunkIndexLength) {
            return filename+String.format(".part%0"+chunkIndexLength+"dof%0"+chunkIndexLength+"d", n+1, total);
        }
        public static String[] splitFile(String fname, long chunkSize) throws IOException {
            FileInputStream fis = null;
            ArrayList<String> res = new ArrayList<String>();
            byte[] buffer = new byte[BUFSIZE];
            try {
                long totalSize = new File(fname).length();
                int nChunks = (int) ((totalSize + chunkSize - 1) / chunkSize);
                int chunkIndexLength = String.format("%d", nChunks).length();
                fis = new FileInputStream(fname);
                long written = 0;
                for (int i=0; written<totalSize; i++) {
                    String chunkFName = chunkFileName(fname, i, nChunks, chunkIndexLength);
                    FileOutputStream fos = new FileOutputStream(chunkFName);
                    try {
                        written += copyStream(fis, buffer, fos, chunkSize);
                    } finally {
                        Closer.closeSilently(fos);
                    }
                    res.add(chunkFName);
                }
            } finally {
                Closer.closeSilently(fis);
            }
            return res.toArray(new String[0]);
        }
        public static boolean canJoinFile(String chunkName) {
            int n = chunkIndexLen(chunkName);
            if (n>0) {
                int nChunks = getNumberOfChunks(chunkName);
                String filename = getWholeFileName(chunkName);
                for (int i=0; i<nChunks; i++) {
                    if (!new File(chunkFileName(filename, i, nChunks, n)).exists()) {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
        public static void joinChunks(String chunkName) throws IOException {
            int n = chunkIndexLen(chunkName);
            if (n>0) {
                int nChunks = getNumberOfChunks(chunkName);
                String filename = getWholeFileName(chunkName);
                byte[] buffer = new byte[BUFSIZE];
                FileOutputStream fos = new FileOutputStream(filename);
                try {
                    for (int i=0; i<nChunks; i++) {
                        FileInputStream fis = new FileInputStream(chunkFileName(filename, i, nChunks, n));
                        try {
                            copyStream(fis, buffer, fos, -1);
                        } finally {
                            Closer.closeSilently(fis);
                        }
                    }
                } finally {
                    Closer.closeSilently(fos);
                }
            }
        }
        public static boolean deleteAllChunks(String chunkName) {
            boolean res = true;
            int n = chunkIndexLen(chunkName);
            if (n>0) {
                int nChunks = getNumberOfChunks(chunkName);
                String filename = getWholeFileName(chunkName);
                for (int i=0; i<nChunks; i++) {
                    File f = new File(chunkFileName(filename, i, nChunks, n));
                    res &= (f.delete() || !f.exists());
                }
            }
            return res;
        }
        private static long copyStream(FileInputStream fis, byte[] buffer, FileOutputStream fos, long maxAmount) throws IOException {
            long chunkSizeWritten;
            for (chunkSizeWritten=0; chunkSizeWritten<maxAmount || maxAmount<0; ) {
                int toRead = maxAmount < 0 ? buffer.length : (int)Math.min(buffer.length, maxAmount - chunkSizeWritten);
                int lengthRead = fis.read(buffer, 0, toRead);
                if (lengthRead < 0) {
                    break;
                }
                fos.write(buffer, 0, lengthRead);
                chunkSizeWritten += lengthRead;
            }
            return chunkSizeWritten;
        }
    }
    

    借用Closerherefrom org.apache.logging.log4j.core.util

    【讨论】:

      【解决方案2】:

      它以用户的形式拆分文件名和目标文件大小(以字节为单位)并将其拆分为子文件,它适用于所有类型的文件,例如(.bin,.jpg,.rar)

      import java.io.*;
      class split{
      public static void main(String args[])throws IOException {
      String a;
      int b;
      long len;
      Console con=System.console();
      System.out.println("Enter File Name: ");
      File f=new File(con.readLine());
      System.out.println("Enter Destination File Size: ");  
      b=Integer.parseInt(con.readLine());
      FileInputStream fis=new FileInputStream(f);
      len=f.length();
      int c=(int)len/b;
      if(((int)len%b)!=0)
      c++;
      for(int i=0;i<c;i++){
      File f1=new File(i+""+"."+f);
      FileOutputStream fos=new FileOutputStream(f1);
      for(int j=0;j<b;j++){   
      int ch;
      if((ch=fis.read())!=-1)
      fos.write(ch); } }
      fis.close();
      System.out.println("Operation Successful"); }}
      

      另一个程序将合并所有拆分文件。它只需要拆分文件名并合并所有文件。

      import java.io.*;
      class merge{
      static int i;
      public static void main(String args[])throws IOException{
      String a;
      int b;
      long len;
      Console con=System.console();
      System.out.println("Enter File to be retrived: ");
      File f=new File(con.readLine());
      FileOutputStream fos=new FileOutputStream(f,true);
      try {
      File f1=new File(i+""+"."+f);
      while((f1.exists())!=false) {
      int ch;
      FileInputStream fis=new FileInputStream(i+""+"."+f);
      i++;
      while((ch=fis.read())!=-1){
      fos.write(ch);  }}}
      catch(FileNotFoundException e1){} }}
      

      【讨论】:

        【解决方案3】:

        分割文件:----->

        import java.io.*;
        
        class Split
        {
        
        
          public static void main(String args[])throws IOException
           {
        
            Console con=System.console();
            System.out.println("enter the file name");
            String path=con.readLine();
            File f= new File(path);
            int filesize=(int)f.length();
            FileInputStream fis= new FileInputStream(path); 
        
            int size;
            System.out.println("enter file size for split");
                size=Integer.parseInt(con.readLine());
        
        
            byte b[]=new byte[size];
        
            int ch,c=0;
        
        
        
        
            while(filesize>0)
                   {
                         ch=fis.read(b,0,size);
        
        
                filesize = filesize-ch;
        
        
                        String fname=c+"."+f.getName()+"";
                c++;
                FileOutputStream fos= new FileOutputStream(new File(fname));
                fos.write(b,0,ch);
                fos.flush();
                fos.close();
        
                }
        
        fis.close();
        
        }
        
        }
        

        【讨论】:

          【解决方案4】:

          当您对文件进行二进制比较时会发生什么。例如与差异。你看到第一个文件后有什么不同吗?

          您可以尝试拆分文本 TXT 文件吗?如果有字节不合适,那么问题应该更明显。例如一个重复的块/文件/或充满 nul 字节的数据。 ??

          编辑:正如其他人所注意到的,您阅读文件的顺序没有特定的顺序。您可以做的是使用填充的文件编号,例如。

          newName = String.format("%s.part%09d", fname, nChunks - 1);
          

          这将为您提供多达 10 亿个按数字顺序排列的文件。

          阅读文件时,需要确保它们已排序。

          Arrays.sort(files);
          for (File file : files) {
          

          使用其他人建议的自定义比较器会减少填充数字的大小,但能够按名称排序以获得正确的顺序会很好。例如在资源管理器中。

          【讨论】:

          • 稍微修正一下:您应该将格式中的“.part”替换为“%s”或删除“.part”参数。
          【解决方案5】:

          是否有超过 10 个块?然后程序将连接 *.part1 + *.part10 + *.part2 等等。

          【讨论】:

          • 非常感谢!非常感谢您的帮助!
          【解决方案6】:

          我只能发现代码中的两个潜在错误:

          int fileSize = (int) ifile.length();
          

          当文件超过 2GB 时,上述操作失败,因为 int 无法容纳更多。

          newName = fname + ".part" + Integer.toString(nChunks - 1);
          

          这样构造的文件名应该以非常特定的方式排序。使用默认字符串排序时,name.part10 将排在name.part2 之前。您想提供一个自定义的 Comparator,它将部件号提取并解析为 int,然后进行比较。

          【讨论】:

          • 该文件远小于 2 GB - 但您对排序部分是正确的,它占用了 0,1,10,11,12...,2,20 等 - 我猜猜这搞砸了
          【解决方案7】:

          对于加入文件,我将所有块的名称放在一个列表中,然后按名称对其进行排序,然后运行以下代码:

          但是你的名字是这样的:

          newName = fname + ".part" + Integer.toString(nChunks - 1);
          

          仔细考虑如果您有 11 个或更多零件会发生什么。哪个字符串按字母顺序排在第一位:“.part10”还是“.part2”? (答案:“.part10”,因为在字符编码中“1”在“2”之前。)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-09-30
            • 1970-01-01
            • 1970-01-01
            • 2015-04-25
            • 1970-01-01
            • 2015-05-26
            相关资源
            最近更新 更多