MD5是加密算法的一种,MD5对于每一个文件来说都是唯一的,在百度网盘、QQ的文件快传中,都是算文件的MD5在本地的服务器上有没有,如果有直接就可以上传。但是有一个场景,就是我的文件在百度上是 违规的,我可以通过修改MD5值,然后上传问文件。

修改文件MD5思路

一般MD5修改的办法,就是修改文件的内容,自然MD5就修改掉了,但是文件这么多,我们也不一定改得了,所以最核心的思路是,将一个文件分成多个文件,然后合并成一个文件,在合并的时候,添加一个空文件,就可以解决这个问题

/*** 给文件生成新的md5验证码* @param pathname 文件的全路径* @param newFileName  新文件的全路径*/
private static void newMD5(String pathname,String newFileName){//分割文件String [] names = FileUtils.partitionFile(pathname);//创建一个空文件String newFile = pathname+".txt";FileUtils.copyStringToFile(UUID.randomUUID().toString(), newFile);//解决 md5 修改String [] newNames = new String[names.length+1];System.out.println(newNames.length);for(int i=0;i<names.length;i++){newNames[i] = names[i];}newNames[names.length] = newFile;//文件合并,多添加了一个空文件FileUtils.uniteFile(newNames, newFileName);//删除所有文件FileUtils.deletes(newNames);
}

文件分割

文件分割操作中,需要根据文件的大小来计算分割的份数,同时也要写计算分割的文件名,其中文件的大小可以自己根据文件的大小计算份数

/*** 文件分割* @param partSize* @param pathname* @return * @throws Exception*/
private static String[] partitionFile(int partSize,String pathname){DataInputStream in  = null;String [] names = null;try {//DataOutputStream//通过DataOutputStream 这个对象完成文件的分割//缓存File file = new File(pathname);//long size = file.length();//1024*1024 就是 1MB//System.out.println(size/1024/1024);//获取分割文件的名称names = getPartitionFileNames(pathname, partSize);in = new DataInputStream( new FileInputStream(file));byte [] buff = new byte[1024];DataOutputStream out = null;for(String name:names){System.out.println(name);out = new DataOutputStream(new FileOutputStream(new File(name)));long size = partSize;while(size >0){//当没有读取完的情况if(in.read(buff) != -1){out.write(buff, 0, buff.length);}size = size-buff.length;}out.close();}} catch (Exception e) {throw new RuntimeException("分割失败");}finally{try {if(in!= null ){in.close();}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return names;
}

计算文件名称

/*** 根据给定的每个文件的大小来获取文件的数目* @param pathname 文件名称* @param partSize 每个部分的大小* @return*/
public static String [] getPartitionFileNames(String pathname,long partSize){File file = new File(pathname);long size = file.length();double num = (size/(double)partSize);//向上取整数,获取分割文件的数量num = Math.ceil(num);String [] names = new String [(int)num] ;for(int i=0;i<num;i++){names[i] = StringUtil.getFileSimpleName(pathname)+"_"+i+"."+StringUtil.getFileTypeByPath(pathname);}return names;
}

文件合并

文件拆分开后,我们需要将所有分开的文件合并,同时包含了空文件,然后删除空文件的操作

/*** 合并文件* @param files 多个子文件* @param newFile 新的文件* @throws Exception 异常*/
public static void uniteFile(String[] files, String newFile) {DataOutputStream out = null;try {out = new DataOutputStream(new FileOutputStream(new File(newFile)));for(String name:files){FileInputStream in = new FileInputStream(new File(name));byte [] buff = new byte[1024];while((in.read(buff)) != -1){out.write(buff,0,buff.length);}in.close();}} catch (Exception e) {throw new RuntimeException(e.getMessage());}finally{try {if(out != null){out.close();}} catch (Exception e2) {// TODO: handle exception}}
}

完整代码

package com.yellowcong.utils;import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;import javax.servlet.http.HttpServletRequest;import org.hibernate.mapping.Array;import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;/*** 文件的 分割 合并* 文件的 md5* 获取文件的 md5* @author yellowcong* @date 2016年4月17日**/
public class FileUtils {//文件夹信息private static List<String> dirNames= null;//文件信息private static List<String> fileNames = null;//文件大小单位 ,我们可以通过添加SizeStr 来添加单位private static String [] sizeStr = {"B","KB","MB","GB","PB"}; private static int count =0;private static long fileSize = 0;private FileUtils(){}/*** 获取类路径下文件的路径* @param fileName* @return*/public static String getClassPathFilePath(String fileName){String path = FileUtils.class.getClassLoader().getResource(fileName).getPath().toString();if(path.contains("%20")){path = path.replace("%20", " ");}return path;}/*** 获取文件对象* @param fileName* @return*/public static File getClassPathFile(String fileName){return new File(FileUtils.getClassPathFilePath(fileName));}/*在类路径创建我们的 文件夹*//*** 创建类路径下面的文件夹* @param directory*/public static String mkClassPathDir(String directory){String path = FileUtils.class.getClassLoader().getResource("").getPath();path += directory;File file = new File(path);if(!file.exists()){file.mkdir();}return path;}/*** 获取类文件的输入流* @param fileName* @return*/public static InputStream getClassPathFileInputStream(String fileName){return FileUtils.class.getResourceAsStream(fileName);}/*** 将File 文件转化为 字符串* @param filePath* @return*/public static String copyFileToString(String filePath){return FileUtils.getFileContent(filePath);}/*** 获取一个文本文件里面的内容* @param filePath* @return*/public static String getFileContent(String filePath){FileReader in  = null;BufferedReader reader = null;try {in  = new FileReader(new File(filePath));reader = new BufferedReader(in);String line = null;StringBuffer str = new StringBuffer();while((line = reader.readLine())!= null){str.append(line);str.append("\r\n");}return str.toString();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{try {if(in != null){in.close();}if(reader != null ){reader.close();}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}return null;}/*** 获取 web目录下的路径,便于上传* @param str* @param request* @return*/public static String getWebFilePath(String str,HttpServletRequest request){//这个直接获取的方法已经过期了
//      return request.getRealPath(str);return request.getSession().getServletContext().getRealPath(str);}/*** 通过 路径来创建文件* @param path*/public static void createDirectory(String path){File file = new File(path);if(!file.exists()){file.mkdirs();}}/*** 通过文件夹  获取文件夹 当前目录 和下面目录所有的文件 * @param path* @param isNowPath 目录级别  当前目录 数据 * @return*///获取所有文件public static List<String> getChildFiles(String path,boolean isNowPath){if(!new File(path).exists()){throw new RuntimeException(path+",文件夹不存在");}if(!new File(path).isDirectory()){throw new RuntimeException(path+",不是文件夹");}fileNames = new ArrayList<String>();listAllFile(path,isNowPath);return fileNames;}/*** 通过文件夹  获取文件夹 当前目录 和下面目录所有的文件 * @param path* @return*/public static List<String> getChildFiles(String path){return getChildFiles(path, false);}/*** 通过文件夹 获取* 所有的文件夹* @param path 目录名称* @param isNowPath 是否是当前目录* @return*/public static List<String> getChildDirs(String path,boolean isNowPath){if(!new File(path).exists()){throw new RuntimeException(path+",文件夹不存在");}if(!new File(path).isDirectory()){throw new RuntimeException(path+",不是文件夹");}dirNames = new ArrayList<String>();listAllDir(path,isNowPath);return dirNames;}/*** 通过文件夹 获取 我们的子 * 所有的文件夹  * @param path 目录名称* @return*/public static List<String> getChildDirs(String path){return getChildDirs(path, false);}/*** 获取某个文件夹下面的文件* 其中文件夹包含了第一个文件夹就是自己 重复调用* * @param path 文件夹路径名称*/private static void listAllFile(String path,boolean isNowPath){File file = new File(path);if(file.isDirectory()){for(File f:file.listFiles()){if(isNowPath){if(f.isFile()){fileNames.add(f.getPath());}}else{listAllFile(f.getPath(),isNowPath);}}}else{fileNames.add(path);}}/*** 获取某个文件夹下面的文件* 其中文件夹包含了第一个文件夹就是自己 重复调用* * @param path 文件夹路径名称* @param isNowPath 是否是当前目录 ,包含子目录*/private static void listAllDir(String path,boolean isNowPath){File file = new File(path);if(file.isDirectory()){dirNames.add(path);for(File f:file.listFiles()){if(isNowPath){dirNames.add(f.getPath());}else{listAllDir(f.getPath(),isNowPath);}}}}/*** 获取所有的文件,包括 文件夹的信息* @param path* @return*/public static List<String> listAllFiles(String path){List<String> strs = FileUtils.getChildDirs(path);strs.addAll(FileUtils.getChildFiles(path));FileUtils.getChildFiles(path);return strs;}/*** 文件分类获取* @param isNowPath 是否 只是当前目录* * String  文件类型  txt doc 等等* List<String> 存的路径* * jpg xxx.xx*     xx.xx* * * @return*/public static Map<String,List<String>> getFilesTypeMap(String path,boolean isNowPath){if(!new File(path).exists()){throw new RuntimeException(path+",文件夹不存在");}List<String> str = FileUtils.getChildFiles(path,isNowPath);Map<String,List<String>> map = new HashMap<String, List<String>>();for(String val:str){String type = StringUtil.getFileTypeByPath(val);if(map.get(type) == null){List<String> names = new ArrayList<String>();names.add(val);map.put(type, names);}else{List<String> names =map.get(type);names.add(val);map.put(type, names);}}return map;}/*** 获取所有目录级别下面的数据* @param path* @return*/public static Map<String,List<String>> getFilesTypeMap(String path){return getFilesTypeMap(path, false);}/*** 获取目录下文件下面 某个类型的数据* @param path* @param type 文件类型* @param isNowPath 是否是 当前目录 , 不是所有 目录* @return*/public static List<String> getFilesByType(String path,boolean isNowPath,String [] types){List<String> list = null;if(types != null && types.length >0){Map<String,List<String>> map = getFilesTypeMap(path,isNowPath);list = new ArrayList<String>();for(String type:types ){if(map.get(type)!= null){list.addAll(map.get(type));}}}else{//如果没有说明 就是所有数据都要listAllFile(path, false);list = fileNames;}return list;}/*** 获取目录下文件下面 某个类型的数据* @param path* @param type 文件类型* @return*/public static List<String> getFilesByType(String path,String [] types){return getFilesByType(path, false, types);}/*** 将InputStream  转化为输出数据* @param in 输入流* @param file 输出的文件对象*/public static void copyInputStreamToFile(InputStream in ,File file){OutputStream out= null;try {out = new FileOutputStream(file);byte[] buff = new byte[1024];int len = 0;while ((len = in.read(buff)) > 0) {out.write(buff, 0, len);}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{try {if(in != null){in.close();}} catch (Exception e2) {// TODO: handle exception}finally{try {if(out != null){out.close();}} catch (Exception e3) {// TODO: handle exception}}}}/*** 将字符数据转化为我们的文件* @param data 字节数组* @param file 输出文件*/public static void copyByte2File(byte [] data,File file){if ((data == null) || (data.length <= 0))return;OutputStream out = null;InputStream in = null;try {out = new FileOutputStream(file);in = new ByteInputStream(data,data.length );byte [] buff = new byte[1024];int len =  0;while((len = in.read(buff))>0){out.write(buff, 0, len);}out.flush();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{try {if(in != null){in.close();}} catch (Exception e2) {// TODO: handle exception}finally{try {if(out != null){out.close();}} catch (Exception e3) {// TODO: handle exception}}}}/*** 将InputStream 转化为字节码文件* @return */public static byte[] copyFileToByteArray(File file){InputStream  in = null;try {in = new FileInputStream(file);//获取字节码的数量,将字节码装int len = in.available();byte [] result = new byte[len];in.read(result);return result;} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{try {if(in != null){in.close();}} catch (Exception e2) {// TODO: handle exception}}return null;}/*** 将InputStream 转化为字节码文件* @return */public static byte[] copyInputStreamToByteArray(InputStream in){try {//获取字节码的数量,将字节码装int len = in.available();byte [] result = new byte[len];in.read(result);return result;} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{try {if(in != null){in.close();}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return null;}/*** xx "B","KB","MB","GB","PB"* * 计算文件大小* @param size*/private static void getFileSize(long size){long result  = size/1024;if(result >=1 && count <(sizeStr.length-2)){fileSize = size/1024;count = count +1;getFileSize(fileSize);}else{fileSize = size;}}/*** * xx "B","KB","MB","GB","PB"* * 计算文件大小* 我们用的时候,对于 静态的数据,需要清空 或者重新初始化,解决多次使用,导致数据堆积问题* @param fileSize* @return*/public static String getFileSizeStr(long filesize){if(filesize <0){throw new RuntimeException("文件大小有问题");}//多次使用的时候,这个 就会用问题了,一定要清空count = 0;fileSize= 0;getFileSize(filesize);return fileSize+sizeStr[count];}/*** 文件分割* String pathname = "E:\\迅雷下载\\疯狂动物城.Zootopia.2016.HDCAM.XVID.AC3.中文字幕[rarbt]\\疯狂动物城.Zootopia.2016.HDCAM.XVID.AC3.中文字幕[rarbt].mp4";* 给定文件进行分割,默认分割的大小是500MB* @return 切割的文件名称* @throws Exception*/public static String[] partitionFile(String pathname){return partitionFile(1024*1024*500,pathname) ;}/*** 文件分割* @param partSize* @param pathname* @return * @throws Exception*/private static String[] partitionFile(int partSize,String pathname){DataInputStream in  = null;String [] names = null;try {//DataOutputStream//通过DataOutputStream 这个对象完成文件的分割//缓存File file = new File(pathname);//long size = file.length();//1024*1024 就是 1MB//System.out.println(size/1024/1024);//获取分割文件的名称names = getPartitionFileNames(pathname, partSize);in = new DataInputStream( new FileInputStream(file));byte [] buff = new byte[1024];DataOutputStream out = null;for(String name:names){System.out.println(name);out = new DataOutputStream(new FileOutputStream(new File(name)));long size = partSize;while(size >0){//当没有读取完的情况if(in.read(buff) != -1){out.write(buff, 0, buff.length);}size = size-buff.length;}out.close();}} catch (Exception e) {throw new RuntimeException("分割失败");}finally{try {if(in!= null ){in.close();}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return names;}/*** 新建md5文件* @param pathname 需要新建md5的文件名称* * 创建的文件在那个目录中,然后文件的名称,添加了一个_md5*/public static void newMD5(String pathname){String newFileName = StringUtil.getFileSimpleName(pathname)+"_yellowcong_md5."+StringUtil.getFileType(pathname);System.err.println("修改前MD5值\t"+FileUtils.getMd5(pathname));FileUtils.newMD5(pathname, newFileName);System.err.println("修改后MD5值\t"+FileUtils.getMd5(newFileName));}/*** 获取文件的MD5* @param filename 文件的名称,全路径的* @return*/public static  String getMd5(String filename) {  String value = null;  FileInputStream in = null;  File file = new File(filename);try {  in = new FileInputStream(file);MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());  MessageDigest md5 = MessageDigest.getInstance("MD5");  md5.update(byteBuffer);  BigInteger bi = new BigInteger(1, md5.digest());  value = bi.toString(16);  } catch (Exception e) {  e.printStackTrace();  } finally {  if(null != in) {  try {  in.close();  } catch (IOException e) {  e.printStackTrace();  }  }  }  return value;  }  /*** 给文件生成新的md5验证码* @param pathname 文件的全路径* @param newFileName  新文件的全路径*/private static void newMD5(String pathname,String newFileName){//String pathname = "E:\\迅雷下载\\疯狂动物城.Zootopia.2016.HDCAM.XVID.AC3.中文字幕[rarbt]\\疯狂动物城.Zootopia.2016.HDCAM.XVID.AC3.中文字幕[rarbt].mp4";//分割文件String [] names = FileUtils.partitionFile(pathname);//创建一个空文件String newFile = pathname+".txt";FileUtils.copyStringToFile(UUID.randomUUID().toString(), newFile);//解决 md5 修改String [] newNames = new String[names.length+1];System.out.println(newNames.length);for(int i=0;i<names.length;i++){newNames[i] = names[i];}newNames[names.length] = newFile;//文件合并,多添加了一个空文件FileUtils.uniteFile(newNames, newFileName);//删除所有文件FileUtils.deletes(newNames);}/*** 删除多个文件* @param fileNames*/public static void deletes(String [] fileNames){//删除文件for(String path:fileNames){new File(path).delete();}}/*** 合并文件* @param files 多个子文件* @param newFile 新的文件* @throws Exception 异常*/public static void uniteFile(String[] files, String newFile) {DataOutputStream out = null;try {out = new DataOutputStream(new FileOutputStream(new File(newFile)));for(String name:files){FileInputStream in = new FileInputStream(new File(name));byte [] buff = new byte[1024];while((in.read(buff)) != -1){out.write(buff,0,buff.length);}in.close();}} catch (Exception e) {throw new RuntimeException(e.getMessage());}finally{try {if(out != null){out.close();}} catch (Exception e2) {// TODO: handle exception}}}/*** 根据给定的每个文件的大小来获取文件的数目* @param pathname 文件名称* @param partSize 每个部分的大小* @return*/public static String [] getPartitionFileNames(String pathname,long partSize){File file = new File(pathname);long size = file.length();double num = (size/(double)partSize);//向上取整数,获取分割文件的数量num = Math.ceil(num);String [] names = new String [(int)num] ;for(int i=0;i<num;i++){names[i] = StringUtil.getFileSimpleName(pathname)+"_"+i+"."+StringUtil.getFileTypeByPath(pathname);}return names;}/*** 将String类型的数据直接转化为文本数据* @param str* @param filename*/public static void copyStringToFile(String str,String filename){FileWriter out = null;try {//将Str 转化为文件out= new FileWriter(new File(filename));char []  chars = str.toCharArray();out.write(chars,0,chars.length);} catch (IOException e) {e.printStackTrace();}finally{try {if(out != null){out.close();}} catch (Exception e) {// TODO: handle exception}}}/*** 将InputStream 的数据转化为String类型的数据* @param in 输入数据* @param encoding  编码方式gb2312,utf-8 * @return String*/public static String copyInput2String(InputStream in,String encoding){String str = null;try {//写数据String line = null;BufferedReader reader = new BufferedReader(new InputStreamReader(in,encoding));StringBuffer sb = new StringBuffer();while((line = reader.readLine())!= null){sb.append(line);}str = sb.toString();} catch (Exception e) {// TODO: handle exception}finally{try {if(in != null){in.close();}} catch (Exception e2) {// TODO: handle exception}}return str;}/*** 将InputStream 的数据转化为String类型的数据,设定默认的编码编码方式为UTF-8* @param in 输入数据* @return String*/public static String copyInput2String(InputStream in){return FileUtils.copyInput2String(in,"UTF-8");}
}

Java修改文件MD5值-yellowcong相关推荐

  1. java 修改文件MD5值

    java 修改文件Md5 介绍 代码示例 介绍 很多平台在上传文件或图片时,用MD5做了去重校验,已经传过的文件就不让传了,但有时候我们偏偏就想重复传.MD5是加密算法的一种,是对文件的加密,那么想办 ...

  2. Java 获取文件md5值校验文件

    假如我们想校验两个文件网络传输中是否改变了,或者校验两个文件是否一致可以使用md5校验. 代码: import java.io.FileInputStream; import java.io.Inpu ...

  3. 修改文件md5值的简单方法!

    转载自:https://jingyan.baidu.com/article/e52e36150ccc3140c60c512e.html cmd命令行下: 同一目录下存在88.jpg  abc.txt ...

  4. JAVA中获取文件MD5值的四种方法

    JAVA中获取文件MD5值的四种方法其实都很类似,因为核心都是通过JAVA自带的MessageDigest类来实现.获取文件MD5值主要分为三个步骤,第一步获取文件的byte信息,第二步通过Messa ...

  5. 监测linux一些重要文件md5值脚本

    今天写了个小小的监测linux重要文件md5值的脚本, 为了安全最好在安装好系统之后部署 其实网上有开源软件 监测文件是否被修改 #!/bin/bash #crontab everyday FILEN ...

  6. STM32计算文件MD5值校验数据

    首先介绍一下什么是MD5: 一.MD5计算将整个文件或者字符串,通过其不可逆的字符串变换计算,产生文件或字符串的MD5散列值.任意两个文件.字符串不会有相同的散列值(即"很大可能" ...

  7. MD5工具类,提供字符串MD5加密(校验)、文件MD5值获取(校验)功能

    import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.Byt ...

  8. 修改视频MD5值怎么修改?这几种方法修改起来很简单

    当我们在互联网上发布视频时,通过修改视频的MD5值,可以更好地保护视频版权,防止其他人在未经授权的情况下复制视频并在其他网站上发布.此外,一些视频平台或软件可能要求视频的MD5值匹配才能上传或播放,并 ...

  9. 基于vue框架下使用Element-UI获取文件MD5值并上传

    基于vue框架下使用Element-UI获取文件MD5值并上传 使用插件: spark-md5 .vue页面 <el-uploadclass="avatar-uploader idca ...

  10. Java生成文件hash值

    Java生成文件hash值(通过传入file或者InputStream) package com.hczy.syncdata.common.util;import java.io.File; impo ...

最新文章

  1. Intellij IDEA 默认打开上次项目设置与取消设置
  2. mfc制作登录界面mysql_MFC制作漂亮界面之登录界面
  3. 如何应对多GPU大规模训练的挑战?
  4. Linux 命令之 tail -- 在屏幕上显示指定文件的末尾若干行/显示文件尾部内容/查看文件尾部内容
  5. IBM T60网卡问题
  6. 列表解析python_Python 列表解析
  7. numpy 数据类型与 Python 原生数据类型
  8. rust里面的awm叫什么_铅笔里面有铅吗?为什么叫铅笔呢?
  9. Win7删除不常用的自带应用程序
  10. 反射使用 非空表向空表赋值
  11. 酒桌遭遇劝酒莫惊慌 挡酒有词咱见招拆招(ZT)
  12. 35KV,110KV变电所设计,供配电电气部分设计
  13. 阿里云“芝麻信用互查”产品接入使用过程中遇到的那些坑以及解决方案
  14. 当yum安装包时显示系统空间不足,求指教
  15. 部署dicuz论坛网站
  16. 翻译 API 一句话API
  17. vivo信号无服务器,vivo X50上手实测,连央视都夸的国货之光究竟体验如何?
  18. 实现用户的登录,并且登录后显示用户名
  19. eis系统 java_Java消息系统介绍 - marvin_vov的个人空间 - OSCHINA - 中文开源技术交流社区...
  20. 使用vimdiff做git的diff与merge工具

热门文章

  1. LPDDR4的ZQ 校准
  2. 维宏云智能工厂系统1.0全面升级,带你体验豪华智能制造
  3. 18-FreeSwitch-配置G729转码
  4. android+键盘键值修改器,键盘按键修改器
  5. 计算机E盘加密软件,u盘加密软件有哪些?电脑文件夹加密软件哪个好?
  6. 逆向Mac版WPS2019解除版本过期限制
  7. 阿里巴巴测试开发工程师面试记录
  8. 有道词典【输入式翻页】
  9. Centos 7 Opencv安装使用
  10. 统计学国内四门B类期刊以及统计学简史