import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.stream.FileImageInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import zt.o.commoncommon.dto.MessageDTO;
import zt.o.commoncommon.utils.CommonUtil;
import zt.o.commoncommon.utils.ConfigUtil;
public class FileUtils {
private final static Logger logger = LoggerFactory
.getLogger(FileUtils.class);
private FileUtils() {
}
/**
* 文件大小
*/
private final static Long MAX_SIZE = 500 * 1024 *1024l; // 500MB
/**
* 单文件下载
*
* @param request HttpServletRequest
* @param relativeFilePath 上传文件保存的相对路径,例如"upload/",注意,末尾的"/"不要丢了
* @param fileName 文件名
* @return MessageDTO
*/
public static MessageDTO doDownload(HttpServletRequest request,
HttpServletResponse response, String relativeFilePath,
String fileName) {
MessageDTO msg = new MessageDTO();
try {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
String filePath = "";
filePath = relativeFilePath + fileName;
File file = new File(filePath);
// 如果文件不存在
if (file == null || !file.exists()) {
msg.setSuccess(false);
msg.setMessage("文件不存在!");
return msg;
}
String fileType = request.getSession().getServletContext()
.getMimeType(fileName);
if (fileType == null) {
fileType = "application/octet-stream";
}
response.setContentType(fileType);
String newFileName = URLEncoder.encode(fileName, "UTF-8");
String userAgent = request.getHeader("User-Agent").toLowerCase();
if (userAgent.indexOf("msie") != -1) {
newFileName = "filename=\"" + newFileName + "\"";
}
// Opera浏览器只能采用filename*
else if (userAgent.indexOf("opera") != -1) {
newFileName = "filename*=UTF-8''" + newFileName;
}
// Safari浏览器,只能采用ISO编码的中文输出
else if (userAgent.indexOf("safari") != -1) {
newFileName = "filename=\""
+ new String(fileName.getBytes("UTF-8"), "ISO8859-1")
+ "\"";
}
// Chrome浏览器,只能采用MimeUtility编码或ISO编码的中文输出
else if (userAgent.indexOf("applewebkit") != -1) {
newFileName = "filename=\""
+ new String(fileName.getBytes("UTF-8"), "ISO8859-1")
+ "\"";
}
// FireFox浏览器,可以使用MimeUtility或filename*或ISO编码的中文输出
else if (userAgent.indexOf("mozilla") != -1) {
newFileName = "filename*=UTF-8''" + newFileName;
}
response.setHeader("Content-disposition", "attachment;"
+ newFileName);
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(
response.getOutputStream());
byte[] buffer = new byte[1024];
int length = 0;
while ((length = bis.read(buffer)) != -1) {
bos.write(buffer, 0, length);
}
if (bis != null)
bis.close();
if (bos != null)
bos.close();
msg.setSuccess(true);
msg.setMessage("下载成功");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return msg;
}
/**
* 单文件下载
*
* @param request HttpServletRequest
* @param relativeFilePath 上传文件保存的相对路径,例如"upload/",注意,末尾的"/"不要丢了
* @param fileName 文件名
* @return MessageDTO
*/
public static MessageDTO download(HttpServletRequest request,
HttpServletResponse response, String relativeFilePath,
String fileName) {
MessageDTO msg = new MessageDTO();
String webRoot = "";//
String IPMORTPATH = ConfigUtil.getConfig("IPMORT_PATH");
if (IPMORTPATH != null && !IPMORTPATH.isEmpty()) {
webRoot = IPMORTPATH;
} else {
msg.setSuccess(false);
msg.setMessage("配置文件中不存在存放图片路径目录");
return msg;
}
return doDownload(request,response,webRoot + relativeFilePath,fileName);
}
/**
* 单文件下载 临时附件
*
* @param request HttpServletRequest
* @param relativeFilePath 上传文件保存的相对路径,例如"upload/",注意,末尾的"/"不要丢了
* @param fileName 文件名
* @return MessageDTO
*/
public static MessageDTO downloadTemp(HttpServletRequest request,
HttpServletResponse response, String relativeFilePath,
String fileName) {
return doDownload(request,response,relativeFilePath,fileName);
}
/**
* 单文件上传,限制大小为10M
* @return MessageDTO
*/
public static MessageDTO doUpload(MultipartFile file,HttpServletRequest request,String relativeUploadPath){
MessageDTO msg = new MessageDTO();
// 设置字符编码
try {
request.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
msg.setSuccess(false);
msg.setMessage("转字符编码失败!");
logger.info(e.getMessage() + ";" + e.getCause() + ";"
+ e.getStackTrace()[0]);
return msg;
}
String allowFileExt = ConfigUtil.getConfig("ALLOW_FILE_EXT");
//文件名
String fileName = file.getOriginalFilename();
if(fileName==null || fileName.equals("")){
fileName = file.getName();
}
if(fileName.indexOf(".")==-1){
msg.setSuccess(false);
msg.setMessage("不支持的文件格式");
return msg;
}
int fileLastIndex = fileName.lastIndexOf(".");
String fileExt = (fileName.substring(fileLastIndex+1, fileName.length())).toLowerCase();
String[] allowFileExtArr = allowFileExt.split(",");
boolean isExist = false;
for (int i = 0; i < allowFileExtArr.length; i++) {
if(fileExt.equals(allowFileExtArr[i])){
isExist = true;
break;
}
}
if(!isExist){
msg.setSuccess(false);
msg.setMessage("不支持的文件格式:"+fileExt);
return msg;
}
// 如果上传文件目录和临时目录不存在则自动创建
if (!new File(relativeUploadPath).isDirectory()) {
new File(relativeUploadPath).mkdirs();
}
if (file.getSize() <= MAX_SIZE) { // 如果是文件
String disfilename = "";
String imageName = "";
imageName = relativeUploadPath + fileName;
FileChannel in = null;
FileChannel out = null;
FileInputStream fileInputStream = null;
File destFile = new File(imageName);
String index = "";
String refileName = fileName;
if (destFile.exists()) {
List<String> filenamelist = getFileList(relativeUploadPath, fileName);
if (filenamelist != null) {
index = filenamelist.size() + "";
disfilename = imageName.substring(0,
imageName.lastIndexOf("."))
+ "("
+ index
+ ")"
+ imageName.substring(imageName
.lastIndexOf("."));
refileName = fileName.substring(0,
fileName.lastIndexOf("."))
+ "("
+ index
+ ")"
+ fileName.substring(fileName.lastIndexOf("."));
destFile = new File(disfilename);
}
}
FileOutputStream fileOutputStream = null;
InputStream is = null;
try {
is = file.getInputStream();
if(is instanceof FileInputStream){
fileInputStream = (FileInputStream) file.getInputStream();
fileOutputStream = new FileOutputStream(destFile, true);
if (fileInputStream != null && fileOutputStream != null) {
in = fileInputStream.getChannel();
out = fileOutputStream.getChannel();
in.transferTo(0, in.size(), out);
msg.setSuccess(true);
msg.setMessage(refileName);
logger.info("上传成功");
} else {
msg.setSuccess(false);
msg.setMessage("文件是空的或发生不可预知的错误");
logger.info("文件是空的或发生不可预知的错误");
return msg;
}
}
else{
//创建一个文件输出流
FileOutputStream out1 = new FileOutputStream((disfilename.equals("")?imageName:disfilename));
//创建一个缓冲区
byte buffer[] = new byte[1024];
//判断输入流中的数据是否已经读完的标识
int len = 0;
//循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
while((len=is.read(buffer))>0){
//使用FileOutputStream输出流将缓冲区的数据写入到指定的文件当中
out1.write(buffer, 0, len);
}
//关闭输出流
out1.close();
msg.setSuccess(true);
msg.setMessage(refileName);
logger.info("上传成功");
}
} catch (IOException e) {
msg.setSuccess(false);
msg.setMessage("复制失败,转换流出错");
e.printStackTrace();
logger.info("复制失败,转换流出错");
return msg;
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
if (fileOutputStream != null) {
fileOutputStream.close();
}
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if(is != null){
is.close();
}
} catch (IOException e) {
msg.setSuccess(false);
msg.setMessage("复制失败,关闭流出错");
e.printStackTrace();
logger.info("复制失败,关闭流出错");
return msg;
}
}
} else {
msg.setSuccess(false);
msg.setMessage("文件大小不能超出500M");
logger.info("文件大小超出500M");
return msg;
}
return msg;
}
/**
* 单文件上传,限制大小为10M
*
* @param request HttpServletRequest
* @param relativeUploadPath 上传文件保存的相对路径,例如"upload/",注意,末尾的"/"不要丢了
* @return MessageDTO
*/
public static MessageDTO upload(MultipartFile file,
HttpServletRequest request, String relativeUploadPath) {
MessageDTO msg = new MessageDTO();
String serverPath = "";
String IPMORTPATH = ConfigUtil.getConfig("IPMORT_PATH");
if (IPMORTPATH != null && !IPMORTPATH.isEmpty()) {
IPMORTPATH = IPMORTPATH.trim();
serverPath = IPMORTPATH.replace("\\", "/");
}else {
msg.setSuccess(false);
msg.setMessage("配置文件中不存在存放图片路径目录");
return msg;
}
return doUpload(file,request,serverPath + relativeUploadPath);
}
/**
* 单文件上传,限制大小为10M,附件临时存放
*
* @param request HttpServletRequest
* @param relativeUploadPath 上传文件保存的相对路径,例如"upload/",注意,末尾的"/"不要丢了
* @param relativeUploadPath 上传文件保存的绝对路径
* @return MessageDTO
*/
public static MessageDTO uploadTemp(MultipartFile file,
HttpServletRequest request, String relativeUploadPath) {
return doUpload(file,request,relativeUploadPath);
}
public static List<String> getFileList(String strPath, String fileName) {
List<String> filelist = new ArrayList<String>();
File dir = new File(strPath);
String tempfileName = "";
File[] files = dir.listFiles(); // 该文件目录下文件全部放入数组
if (files != null) {
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) { // 判断是文件还是文件夹
getFileList(files[i].getAbsolutePath(), fileName); // 获取文件绝对路径
} else {
tempfileName = files[i].getName();
if (tempfileName.equals(fileName)
|| tempfileName.contains(fileName.substring(0,
fileName.lastIndexOf(".")) + "(")) {
filelist.add(tempfileName);
}
}
}
}
return filelist;
}
public static MessageDTO delete(File file, HttpServletRequest request) {
MessageDTO msg = new MessageDTO();
if (file.exists() && file.isFile()) {
if (file.delete()) {
msg.setMessage("删除成功");
msg.setSuccess(true);
}
}
return msg;
}
public static String downloadPicture(String urlString,String savePath) throws IOException {
// 构造URL
String urlStr = "";
URL url = null;
OutputStream os;
BufferedInputStream is;
try {
url = new URL(urlString);
//检查扩展名
String imageName = "";
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String frontName = df.format(new Date()) + "_" + new Random().nextInt(1000)+".";//自定义名称
// 打开连接
URLConnection con = url.openConnection();
// 输入流
is = new BufferedInputStream(con.getInputStream());
String imgType = HttpURLConnection.guessContentTypeFromStream(is);
//先处理识别文件类型为null的情况
String tempType = "";
if(imgType == null){
tempType = urlString.substring(urlString.lastIndexOf(".")+1, urlString.length());
if(!(tempType.contains("gif")||tempType.contains("jpg")||tempType.contains("jpeg")
||tempType.contains("png")||tempType.contains("bmp"))){
return "系统暂不支持"+tempType+"格式图片类型";
}
if(tempType.equals("jpeg")){
tempType = "jpg";
}
imageName = frontName + tempType;
}else{
if(imgType.contains("image/")){
tempType = imgType.substring(imgType.indexOf("/")+1, imgType.length());
if(!(tempType.contains("gif")||tempType.contains("jpg")||tempType.contains("jpeg")
||tempType.contains("png")||tempType.contains("bmp"))){
return "系统暂不支持"+tempType+"格式图片类型";
}
if(tempType.equals("jpeg")){
tempType = "jpg";
}
imageName = frontName + tempType;
}else{
return "系统暂不支持"+imgType+"格式文件类型!";
}
}
List<String> filenamelist = getFileList(savePath, imageName);
String index = "";
if (filenamelist != null && filenamelist.size()>0) {
index = filenamelist.size() + "";
imageName = imageName.substring(0,
imageName.lastIndexOf("."))
+ "("
+ index
+ ")"
+ imageName.substring(imageName
.lastIndexOf("."));
}
// 1K的数据缓冲
byte[] bs = new byte[1024];
// 读取到的数据长度
int len;
// 输出的文件流
File sf=new File(savePath);
if(!sf.exists()){
sf.mkdirs();
}
os = new FileOutputStream(sf.getPath()+"//"+imageName);
// 开始读取
while ((len = is.read(bs)) != -1) {
os.write(bs, 0, len);
}
// 完毕,关闭所有链接
os.close();
is.close();
urlStr = savePath+imageName;
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return urlStr;
}
// 提取图片相关信息
public static List<String> getImgSrc(String htmlStr) {
String img = "";
Pattern p_image;
Matcher m_image;
List<String> pics = new ArrayList<String>();
String regEx_img = "<img.*src\\s*=\\s*(.*?)[^>]*?>";
p_image = Pattern.compile(regEx_img, Pattern.CASE_INSENSITIVE);
m_image = p_image.matcher(htmlStr);
while (m_image.find()) {
img = img + "," + m_image.group();
//匹配src
Matcher m = Pattern.compile("src\\s*=\\s*\"?([^\"]+)\"").matcher(img);
while (m.find()) {
pics.add(m.group(1));
}
}
return pics;
}
/**
* 根据图片路径把图片放入byte数组
* @param path
* @return
*/
public static byte[] imageToByte(String path) {
byte[] data = null;
FileImageInputStream input = null;
try {
input = new FileImageInputStream(new File(path));
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int numBytesRead = 0;
while ((numBytesRead = input.read(buf)) != -1) {
output.write(buf, 0, numBytesRead);
}
data = output.toByteArray();
output.close();
input.close();
} catch (FileNotFoundException ex1) {
ex1.printStackTrace();
} catch (IOException ex1) {
ex1.printStackTrace();
}
return data;
}
/**
* 根据文件路径把文件放入byte数组
* @param path
* @return
*/
public static byte[] fileToByte(String path) {
byte[] data = null;
FileInputStream input = null;
try {
input = new FileInputStream(new File(path));
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int numBytesRead = 0;
while ((numBytesRead = input.read(buf)) != -1) {
output.write(buf, 0, numBytesRead);
}
data = output.toByteArray();
output.close();
input.close();
} catch (FileNotFoundException ex1) {
ex1.printStackTrace();
} catch (IOException ex1) {
ex1.printStackTrace();
}
return data;
}
/**
* 保存文件到指定路径
* @param path
* @return
*/
public static void byteToFile(byte[] data,String filePath) {
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
try {
File dir = new File(filePath);
if(!dir.exists()&&dir.isDirectory()){//判断文件目录是否存在
dir.mkdirs();
}
file = new File(filePath);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(data);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/**
* 复制单个文件
* @param absolutePath 待复制的文件绝对路径
* @param file
* @return
*/
public static MessageDTO copyFileAbsolute(String absolutePath,MultipartFile file) {
MessageDTO messageDTO = new MessageDTO();
if(file!=null && file.getSize() == 0){
messageDTO.setSuccess(false);
messageDTO.setMessage("上传文件不能为空文件");
return messageDTO;
}
CommonsMultipartFile cf= (CommonsMultipartFile)file; //这个myfile是MultipartFile的
DiskFileItem fi = (DiskFileItem)cf.getFileItem() ;
File srcFile = fi.getStoreLocation();
// 判断目标文件是否存在
File destFile = new File(absolutePath);
if (destFile.exists()) {
// 删除已经存在的目标文件,无论目标文件是目录还是单个文件
new File(absolutePath).delete();
} else {
// 如果目标文件所在目录不存在,则创建目录
if (!destFile.getParentFile().exists()) {
// 目标文件所在目录不存在
if (!destFile.getParentFile().mkdirs()) {
// 复制文件失败:创建目标文件所在目录失败
messageDTO.setSuccess(false);
messageDTO.setMessage("复制文件失败:创建目标文件所在目录失败");
return messageDTO;
}
}
}
// 复制文件
FileChannel in = null;
FileChannel out = null;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(srcFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(destFile, true);
} catch (FileNotFoundException e) {
System.out.println("复制失败1");
messageDTO.setSuccess(false);
messageDTO.setMessage("复制文件失败,"+ e.getMessage());
return messageDTO;
}
try {
if (fileInputStream != null && fileOutputStream != null) {
in = fileInputStream.getChannel();
out = fileOutputStream.getChannel();
in.transferTo(0, in.size(), out);
//System.out.println("复制完成");
messageDTO.setSuccess(true);
return messageDTO;
}else{
System.out.println("文件是空的或发生不可预知的错误");
messageDTO.setSuccess(false);
messageDTO.setMessage("获取文件流失败");
return messageDTO;
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("复制失败");
messageDTO.setSuccess(false);
messageDTO.setMessage("复制文件失败,"+ e.getMessage());
return messageDTO;
} finally {
try {
if(null != fileInputStream){
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null != fileOutputStream){
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 复制单个文件
* @param destFileName 待复制的文件相对路径
* @param file 待复制的文件
* @return 如果复制成功返回true,否则返回false
*/
public static MessageDTO copyFile(String destFileName,MultipartFile file) {
destFileName = CommonUtil.getAbsolutePath(destFileName);
if(destFileName==null){
logger.error("IPMORT_PATH为空");
MessageDTO messageDTO = new MessageDTO();
messageDTO.setSuccess(false);
messageDTO.setMessage("获取上传路径为空");
return messageDTO;
}
return copyFileAbsolute(destFileName, file);
}
/**
* 复制单个文件
* @param sourceFileName 源文件相对路径
* @param destFileName 目标文件相对路径
* @return
*/
public static boolean copyFile(String sourceFileName, String destFileName) {
destFileName = CommonUtil.getAbsolutePath(destFileName);
if(destFileName==null){
logger.error("IPMORT_PATH为空");
return false;
}
sourceFileName = CommonUtil.getAbsolutePath(sourceFileName);
File sourceFile = new File(sourceFileName);
if (!sourceFile.exists()) {
logger.info("复制文件失败!源文件不存在:" + sourceFileName);
return false;
}
FileInputStream inStream = null;
FileOutputStream outStream = null;
FileChannel inC = null;
FileChannel outC = null;
try {
File destFile = new File(destFileName);
if (destFile.exists()) {
logger.info("目标文件已存在:" + destFileName);
if (!destFile.delete()) {
logger.info("复制文件失败:删除目标文件" + destFileName + "失败!");
return false;
}
} else {
if (!destFile.getParentFile().exists()) {
// 如果目标文件所在的目录不存在,则创建目录
logger.info("目标文件所在的目录不存在,创建!");
if (!destFile.getParentFile().mkdirs()) {
logger.info("复制文件失败:创建目标文件所在的目录失败!");
return false;
}
}
}
// 复制文件
inStream = new FileInputStream(sourceFileName); // 读入原文件
outStream = new FileOutputStream(destFileName);
if (inStream != null && outStream != null) {
inC = inStream.getChannel();
outC = outStream.getChannel();
inC.transferTo(0, inC.size(), outC);
} else {
logger.info("文件是空的或发生不可预知的错误");
return false;
}
} catch (Exception e) {
logger.info("复制单个文件操作出错:" + sourceFileName);
e.printStackTrace();
} finally {
try {
if (outStream != null) {
outStream.close();
}
if (inStream != null) {
inStream.close();
}
if (inC != null) {
inC.close();
}
if (outC != null) {
outC.close();
}
} catch (IOException e) {
logger.info("复制失败,关闭流出错");
return false;
}
}
return true;
}
/**
* 复制单个文件 通过绝对路径
* @param sourceFileName 源文件相对路径
* @param destFileName 目标文件相对路径
* @return
*/
public static boolean copyFileByAbsolutionPath(String sourceFileName, String destFileName) {
File sourceFile = new File(sourceFileName);
if (!sourceFile.exists()) {
logger.info("复制文件失败!源文件不存在:" + sourceFileName);
return false;
}
FileInputStream inStream = null;
FileOutputStream outStream = null;
FileChannel inC = null;
FileChannel outC = null;
try {
File destFile = new File(destFileName);
if (destFile.exists()) {
logger.info("目标文件已存在:" + destFileName);
if (!destFile.delete()) {
logger.info("复制文件失败:删除目标文件" + destFileName + "失败!");
return false;
}
} else {
if (!destFile.getParentFile().exists()) {
// 如果目标文件所在的目录不存在,则创建目录
logger.info("目标文件所在的目录不存在,创建!");
if (!destFile.getParentFile().mkdirs()) {
logger.info("复制文件失败:创建目标文件所在的目录失败!");
return false;
}
}
}
// 复制文件
inStream = new FileInputStream(sourceFileName); // 读入原文件
outStream = new FileOutputStream(destFileName);
if (inStream != null && outStream != null) {
inC = inStream.getChannel();
outC = outStream.getChannel();
inC.transferTo(0, inC.size(), outC);
} else {
logger.info("文件是空的或发生不可预知的错误");
return false;
}
} catch (Exception e) {
logger.info("复制单个文件操作出错:" + sourceFileName);
e.printStackTrace();
} finally {
try {
if (outStream != null) {
outStream.close();
}
if (inStream != null) {
inStream.close();
}
if (inC != null) {
inC.close();
}
if (outC != null) {
outC.close();
}
} catch (IOException e) {
logger.info("复制失败,关闭流出错");
return false;
}
}
return true;
}
}