File 文件操作类 大全
许多人都会对文件操作感到很难 我也是 但是一个好的项目中必定会涉及到文件操作的
文件的复制 粘贴 等等等
公司大佬写了 一个文件操作的工具类 感觉还是棒棒的啦
代码如下 :
1 /** 2 * Copyright © 2012-2016 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved. 3 */ 4 package com.thinkgem.jeesite.common.utils; 5 6 import java.io.BufferedOutputStream; 7 import java.io.File; 8 import java.io.FileInputStream; 9 import java.io.FileOutputStream; 10 import java.io.IOException; 11 import java.io.InputStream; 12 import java.io.OutputStream; 13 import java.io.RandomAccessFile; 14 import java.util.Enumeration; 15 import java.util.List; 16 17 import javax.servlet.http.HttpServletRequest; 18 import javax.servlet.http.HttpServletResponse; 19 20 import org.apache.tools.zip.ZipEntry; 21 import org.apache.tools.zip.ZipFile; 22 import org.apache.tools.zip.ZipOutputStream; 23 import org.slf4j.Logger; 24 import org.slf4j.LoggerFactory; 25 26 import com.google.common.collect.Lists; 27 28 /** 29 * 文件操作工具类 30 * 实现文件的创建、删除、复制、压缩、解压以及目录的创建、删除、复制、压缩解压等功能 31 * @author ThinkGem 32 * @version 2015-3-16 33 */ 34 public class FileUtils extends org.apache.commons.io.FileUtils { 35 36 private static Logger logger = LoggerFactory.getLogger(FileUtils.class); 37 38 /** 39 * 复制单个文件,如果目标文件存在,则不覆盖 40 * @param srcFileName 待复制的文件名 41 * @param descFileName 目标文件名 42 * @return 如果复制成功,则返回true,否则返回false 43 */ 44 public static boolean copyFile(String srcFileName, String descFileName) { 45 return FileUtils.copyFileCover(srcFileName, descFileName, false); 46 } 47 48 /** 49 * 复制单个文件 50 * @param srcFileName 待复制的文件名 51 * @param descFileName 目标文件名 52 * @param coverlay 如果目标文件已存在,是否覆盖 53 * @return 如果复制成功,则返回true,否则返回false 54 */ 55 public static boolean copyFileCover(String srcFileName, 56 String descFileName, boolean coverlay) { 57 File srcFile = new File(srcFileName); 58 // 判断源文件是否存在 59 if (!srcFile.exists()) { 60 logger.debug("复制文件失败,源文件 " + srcFileName + " 不存在!"); 61 return false; 62 } 63 // 判断源文件是否是合法的文件 64 else if (!srcFile.isFile()) { 65 logger.debug("复制文件失败," + srcFileName + " 不是一个文件!"); 66 return false; 67 } 68 File descFile = new File(descFileName); 69 // 判断目标文件是否存在 70 if (descFile.exists()) { 71 // 如果目标文件存在,并且允许覆盖 72 if (coverlay) { 73 logger.debug("目标文件已存在,准备删除!"); 74 if (!FileUtils.delFile(descFileName)) { 75 logger.debug("删除目标文件 " + descFileName + " 失败!"); 76 return false; 77 } 78 } else { 79 logger.debug("复制文件失败,目标文件 " + descFileName + " 已存在!"); 80 return false; 81 } 82 } else { 83 if (!descFile.getParentFile().exists()) { 84 // 如果目标文件所在的目录不存在,则创建目录 85 logger.debug("目标文件所在的目录不存在,创建目录!"); 86 // 创建目标文件所在的目录 87 if (!descFile.getParentFile().mkdirs()) { 88 logger.debug("创建目标文件所在的目录失败!"); 89 return false; 90 } 91 } 92 } 93 94 // 准备复制文件 95 // 读取的位数 96 int readByte = 0; 97 InputStream ins = null; 98 OutputStream outs = null; 99 try { 100 // 打开源文件 101 ins = new FileInputStream(srcFile); 102 // 打开目标文件的输出流 103 outs = new FileOutputStream(descFile); 104 byte[] buf = new byte[1024]; 105 // 一次读取1024个字节,当readByte为-1时表示文件已经读取完毕 106 while ((readByte = ins.read(buf)) != -1) { 107 // 将读取的字节流写入到输出流 108 outs.write(buf, 0, readByte); 109 } 110 logger.debug("复制单个文件 " + srcFileName + " 到" + descFileName 111 + "成功!"); 112 return true; 113 } catch (Exception e) { 114 logger.debug("复制文件失败:" + e.getMessage()); 115 return false; 116 } finally { 117 // 关闭输入输出流,首先关闭输出流,然后再关闭输入流 118 if (outs != null) { 119 try { 120 outs.close(); 121 } catch (IOException oute) { 122 oute.printStackTrace(); 123 } 124 } 125 if (ins != null) { 126 try { 127 ins.close(); 128 } catch (IOException ine) { 129 ine.printStackTrace(); 130 } 131 } 132 } 133 } 134 135 /** 136 * 复制整个目录的内容,如果目标目录存在,则不覆盖 137 * @param srcDirName 源目录名 138 * @param descDirName 目标目录名 139 * @return 如果复制成功返回true,否则返回false 140 */ 141 public static boolean copyDirectory(String srcDirName, String descDirName) { 142 return FileUtils.copyDirectoryCover(srcDirName, descDirName, 143 false); 144 } 145 146 /** 147 * 复制整个目录的内容 148 * @param srcDirName 源目录名 149 * @param descDirName 目标目录名 150 * @param coverlay 如果目标目录存在,是否覆盖 151 * @return 如果复制成功返回true,否则返回false 152 */ 153 public static boolean copyDirectoryCover(String srcDirName, 154 String descDirName, boolean coverlay) { 155 File srcDir = new File(srcDirName); 156 // 判断源目录是否存在 157 if (!srcDir.exists()) { 158 logger.debug("复制目录失败,源目录 " + srcDirName + " 不存在!"); 159 return false; 160 } 161 // 判断源目录是否是目录 162 else if (!srcDir.isDirectory()) { 163 logger.debug("复制目录失败," + srcDirName + " 不是一个目录!"); 164 return false; 165 } 166 // 如果目标文件夹名不以文件分隔符结尾,自动添加文件分隔符 167 String descDirNames = descDirName; 168 if (!descDirNames.endsWith(File.separator)) { 169 descDirNames = descDirNames + File.separator; 170 } 171 File descDir = new File(descDirNames); 172 // 如果目标文件夹存在 173 if (descDir.exists()) { 174 if (coverlay) { 175 // 允许覆盖目标目录 176 logger.debug("目标目录已存在,准备删除!"); 177 if (!FileUtils.delFile(descDirNames)) { 178 logger.debug("删除目录 " + descDirNames + " 失败!"); 179 return false; 180 } 181 } else { 182 logger.debug("目标目录复制失败,目标目录 " + descDirNames + " 已存在!"); 183 return false; 184 } 185 } else { 186 // 创建目标目录 187 logger.debug("目标目录不存在,准备创建!"); 188 if (!descDir.mkdirs()) { 189 logger.debug("创建目标目录失败!"); 190 return false; 191 } 192 193 } 194 195 boolean flag = true; 196 // 列出源目录下的所有文件名和子目录名 197 File[] files = srcDir.listFiles(); 198 for (int i = 0; i < files.length; i++) { 199 // 如果是一个单个文件,则直接复制 200 if (files[i].isFile()) { 201 flag = FileUtils.copyFile(files[i].getAbsolutePath(), 202 descDirName + files[i].getName()); 203 // 如果拷贝文件失败,则退出循环 204 if (!flag) { 205 break; 206 } 207 } 208 // 如果是子目录,则继续复制目录 209 if (files[i].isDirectory()) { 210 flag = FileUtils.copyDirectory(files[i] 211 .getAbsolutePath(), descDirName + files[i].getName()); 212 // 如果拷贝目录失败,则退出循环 213 if (!flag) { 214 break; 215 } 216 } 217 } 218 219 if (!flag) { 220 logger.debug("复制目录 " + srcDirName + " 到 " + descDirName + " 失败!"); 221 return false; 222 } 223 logger.debug("复制目录 " + srcDirName + " 到 " + descDirName + " 成功!"); 224 return true; 225 226 } 227 228 /** 229 * 230 * 删除文件,可以删除单个文件或文件夹 231 * 232 * @param fileName 被删除的文件名 233 * @return 如果删除成功,则返回true,否是返回false 234 */ 235 public static boolean delFile(String fileName) { 236 File file = new File(fileName); 237 if (!file.exists()) { 238 logger.debug(fileName + " 文件不存在!"); 239 return true; 240 } else { 241 if (file.isFile()) { 242 return FileUtils.deleteFile(fileName); 243 } else { 244 return FileUtils.deleteDirectory(fileName); 245 } 246 } 247 } 248 249 /** 250 * 251 * 删除单个文件 252 * 253 * @param fileName 被删除的文件名 254 * @return 如果删除成功,则返回true,否则返回false 255 */ 256 public static boolean deleteFile(String fileName) { 257 File file = new File(fileName); 258 if (file.exists() && file.isFile()) { 259 if (file.delete()) { 260 logger.debug("删除文件 " + fileName + " 成功!"); 261 return true; 262 } else { 263 logger.debug("删除文件 " + fileName + " 失败!"); 264 return false; 265 } 266 } else { 267 logger.debug(fileName + " 文件不存在!"); 268 return true; 269 } 270 } 271 272 /** 273 * 274 * 删除目录及目录下的文件 275 * 276 * @param dirName 被删除的目录所在的文件路径 277 * @return 如果目录删除成功,则返回true,否则返回false 278 */ 279 public static boolean deleteDirectory(String dirName) { 280 String dirNames = dirName; 281 if (!dirNames.endsWith(File.separator)) { 282 dirNames = dirNames + File.separator; 283 } 284 File dirFile = new File(dirNames); 285 if (!dirFile.exists() || !dirFile.isDirectory()) { 286 logger.debug(dirNames + " 目录不存在!"); 287 return true; 288 } 289 boolean flag = true; 290 // 列出全部文件及子目录 291 File[] files = dirFile.listFiles(); 292 for (int i = 0; i < files.length; i++) { 293 // 删除子文件 294 if (files[i].isFile()) { 295 flag = FileUtils.deleteFile(files[i].getAbsolutePath()); 296 // 如果删除文件失败,则退出循环 297 if (!flag) { 298 break; 299 } 300 } 301 // 删除子目录 302 else if (files[i].isDirectory()) { 303 flag = FileUtils.deleteDirectory(files[i] 304 .getAbsolutePath()); 305 // 如果删除子目录失败,则退出循环 306 if (!flag) { 307 break; 308 } 309 } 310 } 311 312 if (!flag) { 313 logger.debug("删除目录失败!"); 314 return false; 315 } 316 // 删除当前目录 317 if (dirFile.delete()) { 318 logger.debug("删除目录 " + dirName + " 成功!"); 319 return true; 320 } else { 321 logger.debug("删除目录 " + dirName + " 失败!"); 322 return false; 323 } 324 325 } 326 327 /** 328 * 创建单个文件 329 * @param descFileName 文件名,包含路径 330 * @return 如果创建成功,则返回true,否则返回false 331 */ 332 public static boolean createFile(String descFileName) { 333 File file = new File(descFileName); 334 if (file.exists()) { 335 logger.debug("文件 " + descFileName + " 已存在!"); 336 return false; 337 } 338 if (descFileName.endsWith(File.separator)) { 339 logger.debug(descFileName + " 为目录,不能创建目录!"); 340 return false; 341 } 342 if (!file.getParentFile().exists()) { 343 // 如果文件所在的目录不存在,则创建目录 344 if (!file.getParentFile().mkdirs()) { 345 logger.debug("创建文件所在的目录失败!"); 346 return false; 347 } 348 } 349 350 // 创建文件 351 try { 352 if (file.createNewFile()) { 353 logger.debug(descFileName + " 文件创建成功!"); 354 return true; 355 } else { 356 logger.debug(descFileName + " 文件创建失败!"); 357 return false; 358 } 359 } catch (Exception e) { 360 e.printStackTrace(); 361 logger.debug(descFileName + " 文件创建失败!"); 362 return false; 363 } 364 365 } 366 367 /** 368 * 创建目录 369 * @param descDirName 目录名,包含路径 370 * @return 如果创建成功,则返回true,否则返回false 371 */ 372 public static boolean createDirectory(String descDirName) { 373 String descDirNames = descDirName; 374 if (!descDirNames.endsWith(File.separator)) { 375 descDirNames = descDirNames + File.separator; 376 } 377 File descDir = new File(descDirNames); 378 if (descDir.exists()) { 379 logger.debug("目录 " + descDirNames + " 已存在!"); 380 return false; 381 } 382 // 创建目录 383 if (descDir.mkdirs()) { 384 logger.debug("目录 " + descDirNames + " 创建成功!"); 385 return true; 386 } else { 387 logger.debug("目录 " + descDirNames + " 创建失败!"); 388 return false; 389 } 390 391 } 392 393 /** 394 * 写入文件 395 * @param file 要写入的文件 396 */ 397 public static void writeToFile(String fileName, String content, boolean append) { 398 try { 399 FileUtils.write(new File(fileName), content, "utf-8", append); 400 logger.debug("文件 " + fileName + " 写入成功!"); 401 } catch (IOException e) { 402 logger.debug("文件 " + fileName + " 写入失败! " + e.getMessage()); 403 } 404 } 405 406 /** 407 * 写入文件 408 * @param file 要写入的文件 409 */ 410 public static void writeToFile(String fileName, String content, String encoding, boolean append) { 411 try { 412 FileUtils.write(new File(fileName), content, encoding, append); 413 logger.debug("文件 " + fileName + " 写入成功!"); 414 } catch (IOException e) { 415 logger.debug("文件 " + fileName + " 写入失败! " + e.getMessage()); 416 } 417 } 418 419 /** 420 * 压缩文件或目录 421 * @param srcDirName 压缩的根目录 422 * @param fileName 根目录下的待压缩的文件名或文件夹名,其中*或""表示跟目录下的全部文件 423 * @param descFileName 目标zip文件 424 */ 425 public static void zipFiles(String srcDirName, String fileName, 426 String descFileName) { 427 // 判断目录是否存在 428 if (srcDirName == null) { 429 logger.debug("文件压缩失败,目录 " + srcDirName + " 不存在!"); 430 return; 431 } 432 File fileDir = new File(srcDirName); 433 if (!fileDir.exists() || !fileDir.isDirectory()) { 434 logger.debug("文件压缩失败,目录 " + srcDirName + " 不存在!"); 435 return; 436 } 437 String dirPath = fileDir.getAbsolutePath(); 438 File descFile = new File(descFileName); 439 try { 440 ZipOutputStream zouts = new ZipOutputStream(new FileOutputStream( 441 descFile)); 442 if ("*".equals(fileName) || "".equals(fileName)) { 443 FileUtils.zipDirectoryToZipFile(dirPath, fileDir, zouts); 444 } else { 445 File file = new File(fileDir, fileName); 446 if (file.isFile()) { 447 FileUtils.zipFilesToZipFile(dirPath, file, zouts); 448 } else { 449 FileUtils 450 .zipDirectoryToZipFile(dirPath, file, zouts); 451 } 452 } 453 zouts.close(); 454 logger.debug(descFileName + " 文件压缩成功!"); 455 } catch (Exception e) { 456 logger.debug("文件压缩失败:" + e.getMessage()); 457 e.printStackTrace(); 458 } 459 460 } 461 462 /** 463 * 解压缩ZIP文件,将ZIP文件里的内容解压到descFileName目录下 464 * @param zipFileName 需要解压的ZIP文件 465 * @param descFileName 目标文件 466 */ 467 public static boolean unZipFiles(String zipFileName, String descFileName) { 468 String descFileNames = descFileName; 469 if (!descFileNames.endsWith(File.separator)) { 470 descFileNames = descFileNames + File.separator; 471 } 472 try { 473 // 根据ZIP文件创建ZipFile对象 474 ZipFile zipFile = new ZipFile(zipFileName); 475 ZipEntry entry = null; 476 String entryName = null; 477 String descFileDir = null; 478 byte[] buf = new byte[4096]; 479 int readByte = 0; 480 // 获取ZIP文件里所有的entry 481 @SuppressWarnings("rawtypes") 482 Enumeration enums = zipFile.getEntries(); 483 // 遍历所有entry 484 while (enums.hasMoreElements()) { 485 entry = (ZipEntry) enums.nextElement(); 486 // 获得entry的名字 487 entryName = entry.getName(); 488 descFileDir = descFileNames + entryName; 489 if (entry.isDirectory()) { 490 // 如果entry是一个目录,则创建目录 491 new File(descFileDir).mkdirs(); 492 continue; 493 } else { 494 // 如果entry是一个文件,则创建父目录 495 new File(descFileDir).getParentFile().mkdirs(); 496 } 497 File file = new File(descFileDir); 498 // 打开文件输出流 499 OutputStream os = new FileOutputStream(file); 500 // 从ZipFile对象中打开entry的输入流 501 InputStream is = zipFile.getInputStream(entry); 502 while ((readByte = is.read(buf)) != -1) { 503 os.write(buf, 0, readByte); 504 } 505 os.close(); 506 is.close(); 507 } 508 zipFile.close(); 509 logger.debug("文件解压成功!"); 510 return true; 511 } catch (Exception e) { 512 logger.debug("文件解压失败:" + e.getMessage()); 513 return false; 514 } 515 } 516 517 /** 518 * 将目录压缩到ZIP输出流 519 * @param dirPath 目录路径 520 * @param fileDir 文件信息 521 * @param zouts 输出流 522 */ 523 public static void zipDirectoryToZipFile(String dirPath, File fileDir, ZipOutputStream zouts) { 524 if (fileDir.isDirectory()) { 525 File[] files = fileDir.listFiles(); 526 // 空的文件夹 527 if (files.length == 0) { 528 // 目录信息 529 ZipEntry entry = new ZipEntry(getEntryName(dirPath, fileDir)); 530 try { 531 zouts.putNextEntry(entry); 532 zouts.closeEntry(); 533 } catch (Exception e) { 534 e.printStackTrace(); 535 } 536 return; 537 } 538 539 for (int i = 0; i < files.length; i++) { 540 if (files[i].isFile()) { 541 // 如果是文件,则调用文件压缩方法 542 FileUtils 543 .zipFilesToZipFile(dirPath, files[i], zouts); 544 } else { 545 // 如果是目录,则递归调用 546 FileUtils.zipDirectoryToZipFile(dirPath, files[i], 547 zouts); 548 } 549 } 550 } 551 } 552 553 /** 554 * 将文件压缩到ZIP输出流 555 * @param dirPath 目录路径 556 * @param file 文件 557 * @param zouts 输出流 558 */ 559 public static void zipFilesToZipFile(String dirPath, File file, ZipOutputStream zouts) { 560 FileInputStream fin = null; 561 ZipEntry entry = null; 562 // 创建复制缓冲区 563 byte[] buf = new byte[4096]; 564 int readByte = 0; 565 if (file.isFile()) { 566 try { 567 // 创建一个文件输入流 568 fin = new FileInputStream(file); 569 // 创建一个ZipEntry 570 entry = new ZipEntry(getEntryName(dirPath, file)); 571 // 存储信息到压缩文件 572 zouts.putNextEntry(entry); 573 // 复制字节到压缩文件 574 while ((readByte = fin.read(buf)) != -1) { 575 zouts.write(buf, 0, readByte); 576 } 577 zouts.closeEntry(); 578 fin.close(); 579 System.out 580 .println("添加文件 " + file.getAbsolutePath() + " 到zip文件中!"); 581 } catch (Exception e) { 582 e.printStackTrace(); 583 } 584 } 585 } 586 587 /** 588 * 获取待压缩文件在ZIP文件中entry的名字,即相对于跟目录的相对路径名 589 * @param dirPat 目录名 590 * @param file entry文件名 591 * @return 592 */ 593 private static String getEntryName(String dirPath, File file) { 594 String dirPaths = dirPath; 595 if (!dirPaths.endsWith(File.separator)) { 596 dirPaths = dirPaths + File.separator; 597 } 598 String filePath = file.getAbsolutePath(); 599 // 对于目录,必须在entry名字后面加上"/",表示它将以目录项存储 600 if (file.isDirectory()) { 601 filePath += "/"; 602 } 603 int index = filePath.indexOf(dirPaths); 604 605 return filePath.substring(index + dirPaths.length()); 606 } 607 608 /** 609 * 根据“文件名的后缀”获取文件内容类型(而非根据File.getContentType()读取的文件类型) 610 * @param returnFileName 带验证的文件名 611 * @return 返回文件类型 612 */ 613 public static String getContentType(String returnFileName) { 614 String contentType = "application/octet-stream"; 615 if (returnFileName.lastIndexOf(".") < 0) 616 return contentType; 617 returnFileName = returnFileName.toLowerCase(); 618 returnFileName = returnFileName.substring(returnFileName.lastIndexOf(".") + 1); 619 if (returnFileName.equals("html") || returnFileName.equals("htm") || returnFileName.equals("shtml")) { 620 contentType = "text/html"; 621 } else if (returnFileName.equals("apk")) { 622 contentType = "application/vnd.android.package-archive"; 623 } else if (returnFileName.equals("sis")) { 624 contentType = "application/vnd.symbian.install"; 625 } else if (returnFileName.equals("sisx")) { 626 contentType = "application/vnd.symbian.install"; 627 } else if (returnFileName.equals("exe")) { 628 contentType = "application/x-msdownload"; 629 } else if (returnFileName.equals("msi")) { 630 contentType = "application/x-msdownload"; 631 } else if (returnFileName.equals("css")) { 632 contentType = "text/css"; 633 } else if (returnFileName.equals("xml")) { 634 contentType = "text/xml"; 635 } else if (returnFileName.equals("gif")) { 636 contentType = "image/gif"; 637 } else if (returnFileName.equals("jpeg") || returnFileName.equals("jpg")) { 638 contentType = "image/jpeg"; 639 } else if (returnFileName.equals("js")) { 640 contentType = "application/x-javascript"; 641 } else if (returnFileName.equals("atom")) { 642 contentType = "application/atom+xml"; 643 } else if (returnFileName.equals("rss")) { 644 contentType = "application/rss+xml"; 645 } else if (returnFileName.equals("mml")) { 646 contentType = "text/mathml"; 647 } else if (returnFileName.equals("txt")) { 648 contentType = "text/plain"; 649 } else if (returnFileName.equals("jad")) { 650 contentType = "text/vnd.sun.j2me.app-descriptor"; 651 } else if (returnFileName.equals("wml")) { 652 contentType = "text/vnd.wap.wml"; 653 } else if (returnFileName.equals("htc")) { 654 contentType = "text/x-component"; 655 } else if (returnFileName.equals("png")) { 656 contentType = "image/png"; 657 } else if (returnFileName.equals("tif") || returnFileName.equals("tiff")) { 658 contentType = "image/tiff"; 659 } else if (returnFileName.equals("wbmp")) { 660 contentType = "image/vnd.wap.wbmp"; 661 } else if (returnFileName.equals("ico")) { 662 contentType = "image/x-icon"; 663 } else if (returnFileName.equals("jng")) { 664 contentType = "image/x-jng"; 665 } else if (returnFileName.equals("bmp")) { 666 contentType = "image/x-ms-bmp"; 667 } else if (returnFileName.equals("svg")) { 668 contentType = "image/svg+xml"; 669 } else if (returnFileName.equals("jar") || returnFileName.equals("var") 670 || returnFileName.equals("ear")) { 671 contentType = "application/java-archive"; 672 } else if (returnFileName.equals("doc")) { 673 contentType = "application/msword"; 674 } else if (returnFileName.equals("pdf")) { 675 contentType = "application/pdf"; 676 } else if (returnFileName.equals("rtf")) { 677 contentType = "application/rtf"; 678 } else if (returnFileName.equals("xls")) { 679 contentType = "application/vnd.ms-excel"; 680 } else if (returnFileName.equals("ppt")) { 681 contentType = "application/vnd.ms-powerpoint"; 682 } else if (returnFileName.equals("7z")) { 683 contentType = "application/x-7z-compressed"; 684 } else if (returnFileName.equals("rar")) { 685 contentType = "application/x-rar-compressed"; 686 } else if (returnFileName.equals("swf")) { 687 contentType = "application/x-shockwave-flash"; 688 } else if (returnFileName.equals("rpm")) { 689 contentType = "application/x-redhat-package-manager"; 690 } else if (returnFileName.equals("der") || returnFileName.equals("pem") 691 || returnFileName.equals("crt")) { 692 contentType = "application/x-x509-ca-cert"; 693 } else if (returnFileName.equals("xhtml")) { 694 contentType = "application/xhtml+xml"; 695 } else if (returnFileName.equals("zip")) { 696 contentType = "application/zip"; 697 } else if (returnFileName.equals("mid") || returnFileName.equals("midi") 698 || returnFileName.equals("kar")) { 699 contentType = "audio/midi"; 700 } else if (returnFileName.equals("mp3")) { 701 contentType = "audio/mpeg"; 702 } else if (returnFileName.equals("ogg")) { 703 contentType = "audio/ogg"; 704 } else if (returnFileName.equals("m4a")) { 705 contentType = "audio/x-m4a"; 706 } else if (returnFileName.equals("ra")) { 707 contentType = "audio/x-realaudio"; 708 } else if (returnFileName.equals("3gpp") 709 || returnFileName.equals("3gp")) { 710 contentType = "video/3gpp"; 711 } else if (returnFileName.equals("mp4")) { 712 contentType = "video/mp4"; 713 } else if (returnFileName.equals("mpeg") 714 || returnFileName.equals("mpg")) { 715 contentType = "video/mpeg"; 716 } else if (returnFileName.equals("mov")) { 717 contentType = "video/quicktime"; 718 } else if (returnFileName.equals("flv")) { 719 contentType = "video/x-flv"; 720 } else if (returnFileName.equals("m4v")) { 721 contentType = "video/x-m4v"; 722 } else if (returnFileName.equals("mng")) { 723 contentType = "video/x-mng"; 724 } else if (returnFileName.equals("asx") || returnFileName.equals("asf")) { 725 contentType = "video/x-ms-asf"; 726 } else if (returnFileName.equals("wmv")) { 727 contentType = "video/x-ms-wmv"; 728 } else if (returnFileName.equals("avi")) { 729 contentType = "video/x-msvideo"; 730 } 731 return contentType; 732 } 733 734 /** 735 * 向浏览器发送文件下载,支持断点续传 736 * @param file 要下载的文件 737 * @param request 请求对象 738 * @param response 响应对象 739 * @return 返回错误信息,无错误信息返回null 740 */ 741 public static String downFile(File file, HttpServletRequest request, HttpServletResponse response){ 742 return downFile(file, request, response, null); 743 } 744 745 /** 746 * 向浏览器发送文件下载,支持断点续传 747 * @param file 要下载的文件 748 * @param request 请求对象 749 * @param response 响应对象 750 * @param fileName 指定下载的文件名 751 * @return 返回错误信息,无错误信息返回null 752 */ 753 public static String downFile(File file, HttpServletRequest request, HttpServletResponse response, String fileName){ 754 String error = null; 755 if (file != null && file.exists()) { 756 if (file.isFile()) { 757 if (file.length() <= 0) { 758 error = "该文件是一个空文件。"; 759 } 760 if (!file.canRead()) { 761 error = "该文件没有读取权限。"; 762 } 763 } else { 764 error = "该文件是一个文件夹。"; 765 } 766 } else { 767 error = "文件已丢失或不存在!"; 768 } 769 if (error != null){ 770 logger.debug("---------------" + file + " " + error); 771 return error; 772 } 773 774 long fileLength = file.length(); // 记录文件大小 775 long pastLength = 0; // 记录已下载文件大小 776 int rangeSwitch = 0; // 0:从头开始的全文下载;1:从某字节开始的下载(bytes=27000-);2:从某字节开始到某字节结束的下载(bytes=27000-39000) 777 long toLength = 0; // 记录客户端需要下载的字节段的最后一个字节偏移量(比如bytes=27000-39000,则这个值是为39000) 778 long contentLength = 0; // 客户端请求的字节总量 779 String rangeBytes = ""; // 记录客户端传来的形如“bytes=27000-”或者“bytes=27000-39000”的内容 780 RandomAccessFile raf = null; // 负责读取数据 781 OutputStream os = null; // 写出数据 782 OutputStream out = null; // 缓冲 783 byte b[] = new byte[1024]; // 暂存容器 784 785 if (request.getHeader("Range") != null) { // 客户端请求的下载的文件块的开始字节 786 response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT); 787 logger.debug("request.getHeader(\"Range\") = " + request.getHeader("Range")); 788 rangeBytes = request.getHeader("Range").replaceAll("bytes=", ""); 789 if (rangeBytes.indexOf(‘-‘) == rangeBytes.length() - 1) {// bytes=969998336- 790 rangeSwitch = 1; 791 rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf(‘-‘)); 792 pastLength = Long.parseLong(rangeBytes.trim()); 793 contentLength = fileLength - pastLength; // 客户端请求的是 969998336 之后的字节 794 } else { // bytes=1275856879-1275877358 795 rangeSwitch = 2; 796 String temp0 = rangeBytes.substring(0, rangeBytes.indexOf(‘-‘)); 797 String temp2 = rangeBytes.substring(rangeBytes.indexOf(‘-‘) + 1, rangeBytes.length()); 798 pastLength = Long.parseLong(temp0.trim()); // bytes=1275856879-1275877358,从第 1275856879 个字节开始下载 799 toLength = Long.parseLong(temp2); // bytes=1275856879-1275877358,到第 1275877358 个字节结束 800 contentLength = toLength - pastLength; // 客户端请求的是 1275856879-1275877358 之间的字节 801 } 802 } else { // 从开始进行下载 803 contentLength = fileLength; // 客户端要求全文下载 804 } 805 806 // 如果设设置了Content-Length,则客户端会自动进行多线程下载。如果不希望支持多线程,则不要设置这个参数。 响应的格式是: 807 // Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节] 808 // ServletActionContext.getResponse().setHeader("Content- Length", new Long(file.length() - p).toString()); 809 response.reset(); // 告诉客户端允许断点续传多线程连接下载,响应的格式是:Accept-Ranges: bytes 810 if (pastLength != 0) { 811 response.setHeader("Accept-Ranges", "bytes");// 如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置;响应的格式是:HTTP/1.1 200 OK 812 // 不是从最开始下载, 响应的格式是: Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小] 813 logger.debug("---------------不是从开始进行下载!服务器即将开始断点续传..."); 814 switch (rangeSwitch) { 815 case 1: { // 针对 bytes=27000- 的请求 816 String contentRange = new StringBuffer("bytes ").append(new Long(pastLength).toString()).append("-") 817 .append(new Long(fileLength - 1).toString()).append("/").append(new Long(fileLength).toString()).toString(); 818 response.setHeader("Content-Range", contentRange); 819 break; 820 } 821 case 2: { // 针对 bytes=27000-39000 的请求 822 String contentRange = rangeBytes + "/" + new Long(fileLength).toString(); 823 response.setHeader("Content-Range", contentRange); 824 break; 825 } 826 default: { 827 break; 828 } 829 } 830 } else { 831 // 是从开始下载 832 logger.debug("---------------是从开始进行下载!"); 833 } 834 835 try { 836 /*response.addHeader("Content-Disposition", "attachment; filename=\"" + 837 Encodes.urlEncode(StringUtils.isBlank(fileName) ? file.getName() : fileName) + "\""); 838 response.setContentType(getContentType(file.getName())); // set the MIME type.*/ 839 String agent = (String)request.getHeader("USER-AGENT"); 840 if(agent != null && agent.toLowerCase().indexOf("firefox") > 0){ 841 //response.addHeader("Content-Disposition", "attachment; filename=\"" + new String(Base64.encodeBase64(fileName.getBytes("UTF-8")),"iso-8859-1")+ "\""); 842 //response.setCharacterEncoding("utf-8"); 843 //response.addHeader("Content-Disposition", "attachment; filename=\"" + new String(fileName.getBytes("gb2312"),"ISO-8859-1")+ "\""); 844 //String enableFileName = "=?UTF-8?B?\"" + (new String(Base64.encodeBase64(fileName.getBytes("UTF-8")),"UTF-8")) + "\"?="; 845 String enableFileName = "\"" + new String(fileName.getBytes("UTF-8"),"iso-8859-1") + "\""; 846 response.setHeader("Content-Disposition", "attachment; filename=" + enableFileName); 847 }else{ 848 response.addHeader("Content-Disposition", "attachment; filename=\"" + 849 java.net.URLEncoder.encode(StringUtils.isBlank(fileName) ? file.getName() : fileName,"UTF-8") + "\""); 850 } 851 response.setContentType(getContentType(file.getName())); // set the MIME type. 852 response.addHeader("Content-Length", String.valueOf(contentLength)); 853 os = response.getOutputStream(); 854 out = new BufferedOutputStream(os); 855 raf = new RandomAccessFile(file, "r"); 856 try { 857 switch (rangeSwitch) { 858 case 0: { // 普通下载,或者从头开始的下载 同1 859 } 860 case 1: { // 针对 bytes=27000- 的请求 861 raf.seek(pastLength); // 形如 bytes=969998336- 的客户端请求,跳过 969998336 个字节 862 int n = 0; 863 while ((n = raf.read(b, 0, 1024)) != -1) { 864 out.write(b, 0, n); 865 } 866 break; 867 } 868 case 2: { // 针对 bytes=27000-39000 的请求 869 raf.seek(pastLength); // 形如 bytes=1275856879-1275877358 的客户端请求,找到第 1275856879 个字节 870 int n = 0; 871 long readLength = 0; // 记录已读字节数 872 while (readLength <= contentLength - 1024) {// 大部分字节在这里读取 873 n = raf.read(b, 0, 1024); 874 readLength += 1024; 875 out.write(b, 0, n); 876 } 877 if (readLength <= contentLength) { // 余下的不足 1024 个字节在这里读取 878 n = raf.read(b, 0, (int) (contentLength - readLength)); 879 out.write(b, 0, n); 880 } 881 break; 882 } 883 default: { 884 break; 885 } 886 } 887 out.flush(); 888 logger.debug("---------------下载完成!"); 889 } catch (IOException ie) { 890 /** 891 * 在写数据的时候, 对于 ClientAbortException 之类的异常, 892 * 是因为客户端取消了下载,而服务器端继续向浏览器写入数据时, 抛出这个异常,这个是正常的。 893 * 尤其是对于迅雷这种吸血的客户端软件, 明明已经有一个线程在读取 bytes=1275856879-1275877358, 894 * 如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段, 直到有一个线程读取完毕,迅雷会 KILL 895 * 掉其他正在下载同一字节段的线程, 强行中止字节读出,造成服务器抛 ClientAbortException。 896 * 所以,我们忽略这种异常 897 */ 898 logger.debug("提醒:向客户端传输时出现IO异常,但此异常是允许的,有可能客户端取消了下载,导致此异常,不用关心!"); 899 } 900 } catch (Exception e) { 901 logger.error(e.getMessage(), e); 902 } finally { 903 if (out != null) { 904 try { 905 out.close(); 906 } catch (IOException e) { 907 logger.error(e.getMessage(), e); 908 } 909 } 910 if (raf != null) { 911 try { 912 raf.close(); 913 } catch (IOException e) { 914 logger.error(e.getMessage(), e); 915 } 916 } 917 } 918 return null; 919 } 920 921 /** 922 * 修正路径,将 \\ 或 / 等替换为 File.separator 923 * @param path 待修正的路径 924 * @return 修正后的路径 925 */ 926 public static String path(String path){ 927 String p = StringUtils.replace(path, "\\", "/"); 928 p = StringUtils.join(StringUtils.split(p, "/"), "/"); 929 if (!StringUtils.startsWithAny(p, "/") && StringUtils.startsWithAny(path, "\\", "/")){ 930 p += "/"; 931 } 932 if (!StringUtils.endsWithAny(p, "/") && StringUtils.endsWithAny(path, "\\", "/")){ 933 p = p + "/"; 934 } 935 if (path != null && path.startsWith("/")){ 936 p = "/" + p; // linux下路径 937 } 938 return p; 939 } 940 941 /** 942 * 获目录下的文件列表 943 * @param dir 搜索目录 944 * @param searchDirs 是否是搜索目录 945 * @return 文件列表 946 */ 947 public static List<String> findChildrenList(File dir, boolean searchDirs) { 948 List<String> files = Lists.newArrayList(); 949 for (String subFiles : dir.list()) { 950 File file = new File(dir + "/" + subFiles); 951 if (((searchDirs) && (file.isDirectory())) || ((!searchDirs) && (!file.isDirectory()))) { 952 files.add(file.getName()); 953 } 954 } 955 return files; 956 } 957 958 /** 959 * 获取文件扩展名(返回小写) 960 * @param fileName 文件名 961 * @return 例如:test.jpg 返回: jpg 962 */ 963 public static String getFileExtension(String fileName) { 964 if ((fileName == null) || (fileName.lastIndexOf(".") == -1) || (fileName.lastIndexOf(".") == fileName.length() - 1)) { 965 return null; 966 } 967 return StringUtils.lowerCase(fileName.substring(fileName.lastIndexOf(".") + 1)); 968 } 969 970 /** 971 * 获取文件名,不包含扩展名 972 * @param fileName 文件名 973 * @return 例如:d:\files\test.jpg 返回:d:\files\test 974 */ 975 public static String getFileNameWithoutExtension(String fileName) { 976 if ((fileName == null) || (fileName.lastIndexOf(".") == -1)) { 977 return null; 978 } 979 return fileName.substring(0, fileName.lastIndexOf(".")); 980 } 981 }
大家用到那个可以去试一试 真的很好用啦
有不对的地方还请大家 相互交流一下啦
原文地址:https://www.cnblogs.com/zhukaixin/p/9173071.html
时间: 2024-10-07 00:37:22