支持压缩包内图片轮番预览
parent
8cbb1c3bb7
commit
3e5ba7d3ba
|
@ -9,6 +9,7 @@ import org.springframework.stereotype.Component;
|
|||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -21,7 +22,7 @@ import java.util.Map;
|
|||
public class FileUtils {
|
||||
|
||||
final String REDIS_FILE_PREVIEW_PDF_KEY = "converted-preview-pdf-file";
|
||||
|
||||
final String REDIS_FILE_PREVIEW_IMGS_KEY = "converted-preview-imgs-file";//压缩包内图片文件集合
|
||||
@Autowired
|
||||
RedissonClient redissonClient;
|
||||
@Value("${file.dir}")
|
||||
|
@ -30,6 +31,8 @@ public class FileUtils {
|
|||
@Value("${converted.file.charset}")
|
||||
String charset;
|
||||
|
||||
@Value("${simText}")
|
||||
String[] simText;
|
||||
/**
|
||||
* 已转换过的文件集合(redis缓存)
|
||||
* @return
|
||||
|
@ -48,6 +51,30 @@ public class FileUtils {
|
|||
return convertedList.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型)
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
public String typeFromUrl(String url) {
|
||||
String nonPramStr = url.substring(0, url.indexOf("?") != -1 ? url.indexOf("?") : url.length());
|
||||
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
|
||||
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
|
||||
if (listPictureTypes().contains(fileType.toLowerCase())) {
|
||||
fileType = "picture";
|
||||
}
|
||||
if (listArchiveTypes().contains(fileType.toLowerCase())) {
|
||||
fileType = "compress";
|
||||
}
|
||||
if (listOfficeTypes().contains(fileType.toLowerCase())) {
|
||||
fileType = "office";
|
||||
}
|
||||
if (Arrays.asList(simText).contains(fileType.toLowerCase())) {
|
||||
fileType = "simText";
|
||||
}
|
||||
return fileType;
|
||||
}
|
||||
/**
|
||||
* 从url中剥离出文件名
|
||||
* @param url
|
||||
|
@ -129,6 +156,25 @@ public class FileUtils {
|
|||
convertedList.fastPut(fileName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取redis中压缩包内图片文件
|
||||
* @param fileKey
|
||||
* @return
|
||||
*/
|
||||
public List getRedisImgUrls(String fileKey){
|
||||
RMapCache<String, List> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_IMGS_KEY);
|
||||
return convertedList.get(fileKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置redis中压缩包内图片文件
|
||||
* @param fileKey
|
||||
* @param imgs
|
||||
*/
|
||||
public void setRedisImgUrls(String fileKey,List imgs){
|
||||
RMapCache<String, List> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_IMGS_KEY);
|
||||
convertedList.fastPut(fileKey,imgs);
|
||||
}
|
||||
/**
|
||||
* 判断文件编码格式
|
||||
* @param path
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.apache.commons.compress.archivers.zip.ZipFile;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
@ -49,9 +50,11 @@ public class ZipReader {
|
|||
* </p>
|
||||
* @param filePath
|
||||
*/
|
||||
public String readZipFile(String filePath) {
|
||||
public String readZipFile(String filePath,String fileKey) {
|
||||
String archiveSeparator = "/";
|
||||
Map<String, FileNode> appender = Maps.newHashMap();
|
||||
List imgUrls=Lists.newArrayList();
|
||||
String baseUrl= (String) RequestContextHolder.currentRequestAttributes().getAttribute("baseUrl",0);
|
||||
String archiveFileName = fileUtils.getFileNameFromPath(filePath);
|
||||
try {
|
||||
ZipFile zipFile = new ZipFile(filePath, fileUtils.getFileEncodeUTFGBK(filePath));
|
||||
|
@ -73,12 +76,17 @@ public class ZipReader {
|
|||
}
|
||||
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
|
||||
parentName = (level-1) + "_" + parentName;
|
||||
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory);
|
||||
String type=fileUtils.typeFromUrl(childName);
|
||||
if (type.equalsIgnoreCase("picture")){//添加图片文件到图片列表
|
||||
imgUrls.add(baseUrl+childName);
|
||||
}
|
||||
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory,fileKey);
|
||||
addNodes(appender, parentName, node);
|
||||
appender.put(childName, node);
|
||||
}
|
||||
// 开启新的线程处理文件解压
|
||||
executors.submit(new ZipExtractorWorker(entriesToBeExtracted, zipFile, filePath));
|
||||
fileUtils.setRedisImgUrls(fileKey,imgUrls);
|
||||
return new ObjectMapper().writeValueAsString(appender.get(""));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -86,6 +94,8 @@ public class ZipReader {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 排序zipEntries(对原来列表倒序)
|
||||
* @param entries
|
||||
|
@ -99,8 +109,10 @@ public class ZipReader {
|
|||
return Collections.enumeration(sortedEntries);
|
||||
}
|
||||
|
||||
public String unRar(String filePath){
|
||||
public String unRar(String filePath,String fileKey){
|
||||
Map<String, FileNode> appender = Maps.newHashMap();
|
||||
List imgUrls=Lists.newArrayList();
|
||||
String baseUrl= (String) RequestContextHolder.currentRequestAttributes().getAttribute("baseUrl",0);
|
||||
try {
|
||||
Archive archive = new Archive(new File(filePath));
|
||||
List<FileHeader> headers = archive.getFileHeaders();
|
||||
|
@ -123,11 +135,16 @@ public class ZipReader {
|
|||
headersToBeExtracted.add(Collections.singletonMap(childName, header));
|
||||
}
|
||||
String parentName = getLast2FileName(fullName, "\\", archiveFileName);
|
||||
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory);
|
||||
String type=fileUtils.typeFromUrl(childName);
|
||||
if (type.equalsIgnoreCase("picture")){//添加图片文件到图片列表
|
||||
imgUrls.add(baseUrl+childName);
|
||||
}
|
||||
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory,fileKey);
|
||||
addNodes(appender, parentName, node);
|
||||
appender.put(childName, node);
|
||||
}
|
||||
executors.submit(new RarExtractorWorker(headersToBeExtracted, archive, filePath));
|
||||
fileUtils.setRedisImgUrls(fileKey,imgUrls);
|
||||
return new ObjectMapper().writeValueAsString(appender.get(""));
|
||||
} catch (RarException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -213,9 +230,12 @@ public class ZipReader {
|
|||
private String fileName;
|
||||
private String parentFileName;
|
||||
private boolean directory;
|
||||
|
||||
private String fileKey;//用于图片预览时寻址
|
||||
private List<FileNode> childList;
|
||||
|
||||
public FileNode() {
|
||||
}
|
||||
|
||||
public FileNode(String originName, String fileName, String parentFileName, List<FileNode> childList, boolean directory) {
|
||||
this.originName = originName;
|
||||
this.fileName = fileName;
|
||||
|
@ -223,6 +243,21 @@ public class ZipReader {
|
|||
this.childList = childList;
|
||||
this.directory = directory;
|
||||
}
|
||||
public FileNode(String originName, String fileName, String parentFileName, List<FileNode> childList, boolean directory,String fileKey) {
|
||||
this.originName = originName;
|
||||
this.fileName = fileName;
|
||||
this.parentFileName = parentFileName;
|
||||
this.childList = childList;
|
||||
this.directory = directory;
|
||||
this.fileKey=fileKey;
|
||||
}
|
||||
public String getFileKey() {
|
||||
return fileKey;
|
||||
}
|
||||
|
||||
public void setFileKey(String fileKey) {
|
||||
this.fileKey = fileKey;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.yudianbank.web.controller;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.yudianbank.param.ReturnResponse;
|
||||
import com.yudianbank.utils.*;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -22,6 +23,8 @@ import java.net.URL;
|
|||
import java.net.URLConnection;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author yudian-it
|
||||
|
@ -38,30 +41,36 @@ public class OnlinePreviewController {
|
|||
ZipReader zipReader;
|
||||
@Autowired
|
||||
SimTextUtil simTextUtil;
|
||||
@Value("${simText}")
|
||||
String[] simText;
|
||||
|
||||
@Value("${file.dir}")
|
||||
String fileDir;
|
||||
|
||||
|
||||
/**
|
||||
* @param url
|
||||
* @param model
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value = "onlinePreview",method = RequestMethod.GET)
|
||||
@RequestMapping(value = "onlinePreview", method = RequestMethod.GET)
|
||||
public String onlinePreview(String url, Model model, HttpServletRequest req) throws UnsupportedEncodingException {
|
||||
// 路径转码
|
||||
String decodedUrl = URLDecoder.decode(url, "utf-8");
|
||||
String type = typeFromUrl(url);
|
||||
String type = fileUtils.typeFromUrl(url);
|
||||
String suffix = suffixFromUrl(url);
|
||||
// 抽取文件并返回文件列表
|
||||
String fileName = fileUtils.getFileNameFromURL(decodedUrl);
|
||||
model.addAttribute("fileType", suffix);
|
||||
if (type.equalsIgnoreCase("picture")) {
|
||||
model.addAttribute("imgurl", url);
|
||||
List imgUrls = Lists.newArrayList(url);
|
||||
try{
|
||||
String fileKey = req.getParameter("fileKey");
|
||||
imgUrls.clear();
|
||||
imgUrls.addAll(fileUtils.getRedisImgUrls(fileKey));
|
||||
}catch (Exception e){
|
||||
imgUrls = Lists.newArrayList(url);
|
||||
}
|
||||
model.addAttribute("imgurls", imgUrls);
|
||||
return "picture";
|
||||
} else if (type.equalsIgnoreCase("simText")){
|
||||
} else if (type.equalsIgnoreCase("simText")) {
|
||||
ReturnResponse<String> response = simTextUtil.readSimText(decodedUrl, fileName);
|
||||
if (0 != response.getCode()) {
|
||||
model.addAttribute("msg", response.getMsg());
|
||||
|
@ -69,10 +78,10 @@ public class OnlinePreviewController {
|
|||
}
|
||||
model.addAttribute("ordinaryUrl", response.getMsg());
|
||||
return "txt";
|
||||
} else if(type.equalsIgnoreCase("pdf")){
|
||||
model.addAttribute("pdfUrl",url);
|
||||
} else if (type.equalsIgnoreCase("pdf")) {
|
||||
model.addAttribute("pdfUrl", url);
|
||||
return "pdf";
|
||||
} else if(type.equalsIgnoreCase("compress")){
|
||||
} else if (type.equalsIgnoreCase("compress")) {
|
||||
String fileTree = null;
|
||||
// 判断文件名是否存在(redis缓存读取)
|
||||
if (!StringUtils.hasText(fileUtils.getConvertedFile(fileName))) {
|
||||
|
@ -82,28 +91,25 @@ public class OnlinePreviewController {
|
|||
return "fileNotSupported";
|
||||
}
|
||||
String filePath = response.getContent();
|
||||
if ("zip".equalsIgnoreCase(suffix)
|
||||
|| "jar".equalsIgnoreCase(suffix)
|
||||
|| "gzip".equalsIgnoreCase(suffix)) {
|
||||
fileTree = zipReader.readZipFile(filePath);
|
||||
if ("zip".equalsIgnoreCase(suffix) || "jar".equalsIgnoreCase(suffix) || "gzip".equalsIgnoreCase(suffix)) {
|
||||
fileTree = zipReader.readZipFile(filePath, fileName);
|
||||
} else if ("rar".equalsIgnoreCase(suffix)) {
|
||||
fileTree = zipReader.unRar(filePath);
|
||||
fileTree = zipReader.unRar(filePath, fileName);
|
||||
}
|
||||
fileUtils.addConvertedFile(fileName, fileTree);
|
||||
}else {
|
||||
} else {
|
||||
fileTree = fileUtils.getConvertedFile(fileName);
|
||||
}
|
||||
System.out.println("返回文件tree》》》》》》》》》》》》》》》》》》》");
|
||||
if (null != fileTree) {
|
||||
model.addAttribute("fileTree",fileTree);
|
||||
model.addAttribute("fileTree", fileTree);
|
||||
return "compress";
|
||||
}else {
|
||||
} else {
|
||||
model.addAttribute("msg", "压缩文件类型不受支持,尝试在压缩的时候选择RAR4格式");
|
||||
return "fileNotSupported";
|
||||
}
|
||||
} else if ("office".equalsIgnoreCase(type)) {
|
||||
boolean isHtml = suffix.equalsIgnoreCase("xls")
|
||||
|| suffix.equalsIgnoreCase("xlsx");
|
||||
|| suffix.equalsIgnoreCase("xlsx");
|
||||
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf");
|
||||
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
|
||||
if (!fileUtils.listConvertedFiles().containsKey(pdfName)) {
|
||||
|
@ -133,47 +139,44 @@ public class OnlinePreviewController {
|
|||
}
|
||||
model.addAttribute("pdfUrl", pdfName);
|
||||
return isHtml ? "html" : "pdf";
|
||||
}else {
|
||||
} else {
|
||||
model.addAttribute("msg", "系统还不支持该格式文件的在线预览," +
|
||||
"如有需要请按下方显示的邮箱地址联系系统维护人员");
|
||||
return "fileNotSupported";
|
||||
}
|
||||
}
|
||||
|
||||
private String suffixFromUrl(String url) {
|
||||
String nonPramStr = url.substring(0, url.indexOf("?") != -1 ? url.indexOf("?"): url.length());
|
||||
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
|
||||
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
|
||||
return fileType;
|
||||
/**
|
||||
* 多图片切换预览
|
||||
*
|
||||
* @param model
|
||||
* @param req
|
||||
* @return
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
@RequestMapping(value = "picturesPreview", method = RequestMethod.GET)
|
||||
public String picturesPreview(String urls, Model model, HttpServletRequest req) throws UnsupportedEncodingException {
|
||||
// 路径转码
|
||||
String decodedUrl = URLDecoder.decode(urls, "utf-8");
|
||||
// 抽取文件并返回文件列表
|
||||
String[] imgs = decodedUrl.split("|");
|
||||
List imgurls = Arrays.asList(imgs);
|
||||
model.addAttribute("imgurls", imgurls);
|
||||
return "picture";
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型)
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private String typeFromUrl(String url) {
|
||||
String nonPramStr = url.substring(0, url.indexOf("?") != -1 ? url.indexOf("?"): url.length());
|
||||
|
||||
private String suffixFromUrl(String url) {
|
||||
String nonPramStr = url.substring(0, url.indexOf("?") != -1 ? url.indexOf("?") : url.length());
|
||||
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
|
||||
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
|
||||
if (fileUtils.listPictureTypes().contains(fileType.toLowerCase())) {
|
||||
fileType = "picture";
|
||||
}
|
||||
if (fileUtils.listArchiveTypes().contains(fileType.toLowerCase())) {
|
||||
fileType = "compress";
|
||||
}
|
||||
if (fileUtils.listOfficeTypes().contains(fileType.toLowerCase())) {
|
||||
fileType = "office";
|
||||
}
|
||||
if (Arrays.asList(simText).contains(fileType.toLowerCase())) {
|
||||
fileType = "simText";
|
||||
}
|
||||
return fileType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据url获取文件内容
|
||||
* 当pdfjs读取存在跨域问题的文件时将通过此接口读取
|
||||
*
|
||||
* @param urlPath
|
||||
* @param resp
|
||||
*/
|
||||
|
@ -182,21 +185,21 @@ public class OnlinePreviewController {
|
|||
InputStream inputStream = null;
|
||||
try {
|
||||
String strUrl = urlPath.trim();
|
||||
URL url=new URL(strUrl);
|
||||
URL url = new URL(strUrl);
|
||||
//打开请求连接
|
||||
URLConnection connection = url.openConnection();
|
||||
HttpURLConnection httpURLConnection=(HttpURLConnection) connection;
|
||||
HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
|
||||
httpURLConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
|
||||
inputStream = httpURLConnection.getInputStream();
|
||||
byte[] bs = new byte[1024];
|
||||
int len;
|
||||
while(-1 != (len = inputStream.read(bs))) {
|
||||
while (-1 != (len = inputStream.read(bs))) {
|
||||
resp.getOutputStream().write(bs, 0, len);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if(inputStream != null) {
|
||||
if (inputStream != null) {
|
||||
IOUtils.closeQuietly(inputStream);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
env_base_config = {
|
||||
server_base_url:'http://127.0.0.1:8012/',
|
||||
}
|
||||
env_config = {
|
||||
server_base_url:env_base_config.server_base_url,
|
||||
server_preview_url:env_base_config.server_base_url + 'onlinePreview?url=',
|
||||
server_delete_url:env_base_config.server_base_url + 'deleteFile?fileName=',
|
||||
}
|
|
@ -69,7 +69,7 @@
|
|||
fulls += ",resizable"; // 对于不支持screen属性的浏览器,可以手工进行最大化。 manually
|
||||
}
|
||||
window.open("onlinePreview?url="
|
||||
+ encodeURIComponent("${baseUrl}" + treeNode.fileName), "_blank",fulls);
|
||||
+ encodeURIComponent("${baseUrl}" + treeNode.fileName)+"&fileKey="+treeNode.fileKey, "_blank",fulls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapseOne" class="panel-collapse collapse in">
|
||||
<div id="collapseOne" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<div>
|
||||
如果你的项目需要接入文件预览项目,达到对docx、excel、ppt、jpg等文件的预览效果,那么通过在你的项目中加入下面的代码就可以
|
||||
|
@ -37,10 +37,19 @@
|
|||
};
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
新增多图片同时预览功能,接口如下:
|
||||
<pre style="background-color: #2f332a;color: #cccccc">
|
||||
var fileUrl =url1+"|"+"url2";//多文件使用“|”字符隔开
|
||||
var url = "http://localhost:8012/picturesPreview?urls" + encodeURIComponent(fileUrl);
|
||||
var winHeight = window.document.documentElement.clientHeight-10;
|
||||
$window.open(url, "_blank", "height=" + winHeight
|
||||
+ ",top=80,left=80,toolbar=no, menubar=no, scrollbars=yes, resizable=yes");
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
|
@ -65,6 +74,31 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion"
|
||||
href="#collapseThree">
|
||||
更新记录
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapseThree" class="panel-collapse collapse in">
|
||||
<div class="panel-body">
|
||||
<div>
|
||||
2018年01月12日 :<br>
|
||||
1.新增多图片同时预览<br>
|
||||
2.支持压缩包内图片轮番预览<br><br>
|
||||
|
||||
2018年01月02日 :<br>
|
||||
1.修复txt等文本编码问题导致预览乱码<br>
|
||||
2.修复项目模块依赖引入不到的问题<br>
|
||||
3.新增spring boot profile,支持多环境配置<br>
|
||||
4.引入pdf.js预览doc等文件,支持doc标题生成pdf预览菜单,支持手机端预览<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="loading_container">
|
||||
|
|
|
@ -12,25 +12,16 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>如果图片质量很好,首次加载图片时间可能会有点长,请耐心等待</h1>
|
||||
|
||||
<ul id="dowebok">
|
||||
<#--<li><img id="Imgbox" src="#" width="800px" height="550px"></li>-->
|
||||
|
||||
<li><img id="Imgbox" src="#" width="800px" height="auto"></li>
|
||||
|
||||
<#list imgurls as img>
|
||||
<li><img url="${img}" src="${img}" width="800px" height="auto"></li>
|
||||
</#list>
|
||||
</ul>
|
||||
<script src="js/viewer.min.js"></script>
|
||||
<script>
|
||||
//初始化图片地址
|
||||
window.onload = function () {
|
||||
// document.getElementById("Imgbox").src = getParameter("imgurl");
|
||||
document.getElementById("Imgbox").src =document.getElementById("url").value;
|
||||
}
|
||||
var viewer = new Viewer(document.getElementById('dowebok'), {url: 'src'});
|
||||
viewer.show();
|
||||
</script>
|
||||
<input name="url" value="${imgurl}" type="hidden" id="url" >
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue