mirror of
https://github.com/LamGC/ContentGrabbingJi.git
synced 2025-04-29 22:27:33 +00:00
[Change #5] 优化'getImageById'从文件读入缓存时, 检查图片完整性的速度;
This commit is contained in:
parent
ef5651be47
commit
223d78dbd6
@ -4,7 +4,6 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import net.lamgc.cgj.bot.BotCode;
|
||||
import net.lamgc.cgj.bot.BotCommandProcess;
|
||||
import net.lamgc.cgj.bot.SettingProperties;
|
||||
@ -15,17 +14,18 @@ import net.lamgc.cgj.pixiv.PixivURL;
|
||||
import net.lamgc.cgj.util.URLs;
|
||||
import net.lamgc.utils.base.runner.Argument;
|
||||
import net.lamgc.utils.base.runner.Command;
|
||||
import net.lamgc.utils.encrypt.MessageDigestUtils;
|
||||
import net.lz1998.cq.utils.CQCode;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpHead;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -39,6 +39,10 @@ public final class CacheStoreCentral {
|
||||
|
||||
private final static Hashtable<String, File> imageCache = new Hashtable<>();
|
||||
|
||||
private final static JsonRedisCacheStore imageChecksumCache =
|
||||
new JsonRedisCacheStore(BotGlobal.getGlobal().getRedisServer(),
|
||||
"imageChecksum", BotGlobal.getGlobal().getGson());
|
||||
|
||||
/**
|
||||
* 作品信息缓存 - 不过期
|
||||
*/
|
||||
@ -142,30 +146,29 @@ public final class CacheStoreCentral {
|
||||
downloadLink.substring(downloadLink.lastIndexOf("/") + 1));
|
||||
log.debug("FileName: {}, DownloadLink: {}", fileName, downloadLink);
|
||||
if(!imageCache.containsKey(fileName)) {
|
||||
if(imageFile.exists()) {
|
||||
HttpHead headRequest = new HttpHead(downloadLink);
|
||||
headRequest.addHeader("Referer", PixivURL.getPixivRefererLink(illustId));
|
||||
HttpResponse headResponse;
|
||||
try {
|
||||
headResponse = BotGlobal.getGlobal().getPixivDownload().getHttpClient().execute(headRequest);
|
||||
} catch (IOException e) {
|
||||
log.error("获取图片大小失败!", e);
|
||||
return "图片获取失败!";
|
||||
}
|
||||
String contentLengthStr = headResponse
|
||||
.getFirstHeader(HttpHeaderNames.CONTENT_LENGTH.toString())
|
||||
.getValue();
|
||||
log.debug("图片大小: {}B", contentLengthStr);
|
||||
if (imageFile.length() == Long.parseLong(contentLengthStr)) {
|
||||
imageCache.put(URLs.getResourceName(downloadLink), imageFile);
|
||||
log.debug("作品Id {} 第 {} 页缓存已补充.", illustId, pageIndex);
|
||||
return getImageToBotCode(imageFile, false).toString();
|
||||
if(imageFile.exists() && imageFile.isFile()) {
|
||||
ImageChecksum imageChecksum = getImageChecksum(illustId, pageIndex);
|
||||
if(imageChecksum != null) {
|
||||
try {
|
||||
log.debug("正在检查作品Id {} 第 {} 页图片文件 {} ...", illustId, pageIndex, imageFile.getName());
|
||||
if (ImageChecksum.checkFile(imageChecksum, Files.readAllBytes(imageFile.toPath()))) {
|
||||
imageCache.put(URLs.getResourceName(downloadLink), imageFile);
|
||||
log.debug("作品Id {} 第 {} 页缓存已补充.", illustId, pageIndex);
|
||||
return getImageToBotCode(imageFile, false).toString();
|
||||
} else {
|
||||
log.warn("图片文件 {} 校验失败, 重新下载图片...", imageFile.getName());
|
||||
}
|
||||
} catch(IOException e) {
|
||||
log.error("文件检验时读取失败, 重新下载文件...(file: {})", imageFile.getPath());
|
||||
}
|
||||
} else {
|
||||
log.warn("图片存在但校验不存在, 重新下载图片...");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Throwable throwable = ImageCacheStore.executeCacheRequest(
|
||||
new ImageCacheObject(imageCache, illustId, downloadLink, imageFile));
|
||||
new ImageCacheObject(imageCache, illustId, pageIndex, downloadLink, imageFile));
|
||||
if(throwable != null) {
|
||||
throw throwable;
|
||||
}
|
||||
@ -457,6 +460,20 @@ public final class CacheStoreCentral {
|
||||
return resultBody;
|
||||
}
|
||||
|
||||
protected static ImageChecksum getImageChecksum(int illustId, int pageIndex) {
|
||||
String cacheKey = illustId + ":" + pageIndex;
|
||||
if(!imageChecksumCache.exists(cacheKey)) {
|
||||
return null;
|
||||
} else {
|
||||
return ImageChecksum.fromJsonObject(imageChecksumCache.getCache(cacheKey).getAsJsonObject());
|
||||
}
|
||||
}
|
||||
|
||||
protected static void setImageChecksum(ImageChecksum checksum) {
|
||||
String cacheKey = checksum.getIllustId() + ":" + checksum.getPage();
|
||||
imageChecksumCache.update(cacheKey, ImageChecksum.toJsonObject(checksum), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并String并存取到常量池, 以保证对象一致
|
||||
* @param keys String对象
|
||||
@ -469,4 +486,150 @@ public final class CacheStoreCentral {
|
||||
}
|
||||
return sb.toString().intern();
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片检验信息
|
||||
*/
|
||||
public static class ImageChecksum implements Serializable {
|
||||
|
||||
private final static MessageDigestUtils.Algorithm ALGORITHM = MessageDigestUtils.Algorithm.SHA256;
|
||||
|
||||
private ImageChecksum() {}
|
||||
|
||||
private int illustId;
|
||||
|
||||
private int page;
|
||||
|
||||
private String fileName;
|
||||
|
||||
private long size;
|
||||
|
||||
private byte[] checksum;
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public byte[] getChecksum() {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
public void setChecksum(byte[] checksum) {
|
||||
this.checksum = checksum;
|
||||
}
|
||||
|
||||
public int getIllustId() {
|
||||
return illustId;
|
||||
}
|
||||
|
||||
public void setIllustId(int illustId) {
|
||||
this.illustId = illustId;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public int getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(int page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public static ImageChecksum buildImageChecksumFromStream(
|
||||
int illustId, int pageIndex,
|
||||
String fileName, InputStream imageStream) throws IOException {
|
||||
ImageChecksum checksum = new ImageChecksum();
|
||||
checksum.setIllustId(illustId);
|
||||
checksum.setPage(pageIndex);
|
||||
checksum.setFileName(fileName);
|
||||
ByteArrayOutputStream bufferStream = new ByteArrayOutputStream();
|
||||
checksum.setSize(IOUtils.copyLarge(imageStream, bufferStream));
|
||||
checksum.setChecksum(
|
||||
MessageDigestUtils.encrypt(bufferStream.toByteArray(), ALGORITHM));
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将图片检验信息转换成JsonObject
|
||||
* @param checksum 检验信息对象
|
||||
* @return 转换后的JsonObject对象
|
||||
*/
|
||||
public static JsonObject toJsonObject(ImageChecksum checksum) {
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("illustId", checksum.getIllustId());
|
||||
result.addProperty("page", checksum.getPage());
|
||||
result.addProperty("fileName", checksum.getFileName());
|
||||
result.addProperty("size", checksum.getSize());
|
||||
result.addProperty("checksum", Base64.getEncoder().encodeToString(checksum.getChecksum()));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JsonObject转换到图片检验信息
|
||||
* @param checksumObject JsonObject对象
|
||||
* @return 转换后的图片检验信息对象
|
||||
*/
|
||||
public static ImageChecksum fromJsonObject(JsonObject checksumObject) {
|
||||
ImageChecksum checksum = new ImageChecksum();
|
||||
checksum.setIllustId(checksumObject.get("illustId").getAsInt());
|
||||
checksum.setPage(checksumObject.get("page").getAsInt());
|
||||
checksum.setFileName(checksumObject.get("fileName").getAsString());
|
||||
checksum.setSize(checksumObject.get("size").getAsLong());
|
||||
checksum.setChecksum(Base64.getDecoder().decode(checksumObject.get("checksum").getAsString()));
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比对图片文件是否完整.
|
||||
* @param checksum 图片检验信息
|
||||
* @param imageData 图片数据
|
||||
* @return 如果检验成功, 则返回true
|
||||
*/
|
||||
public static boolean checkFile(ImageChecksum checksum, byte[] imageData) {
|
||||
byte[] sha256Checksum = MessageDigestUtils.encrypt(imageData, ALGORITHM);
|
||||
return checksum.getSize() == imageData.length &&
|
||||
Arrays.equals(checksum.getChecksum(), sha256Checksum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImageChecksum{" +
|
||||
"illustId=" + illustId +
|
||||
", page=" + page +
|
||||
", fileName='" + fileName + '\'' +
|
||||
", size=" + size +
|
||||
", checksum=" + Base64.getEncoder().encodeToString(getChecksum()) +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ImageChecksum checksum1 = (ImageChecksum) o;
|
||||
return illustId == checksum1.illustId &&
|
||||
page == checksum1.page &&
|
||||
size == checksum1.size &&
|
||||
Objects.equals(fileName, checksum1.fileName) &&
|
||||
Arrays.equals(checksum, checksum1.checksum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(illustId, page, fileName, size);
|
||||
result = 31 * result + Arrays.hashCode(checksum);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,7 @@ import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@ -69,8 +67,20 @@ public class ImageCacheHandler implements EventHandler {
|
||||
}
|
||||
|
||||
log.debug("正在下载...(Content-Length: {}KB)", response.getEntity().getContentLength() / 1024);
|
||||
try(FileOutputStream fos = new FileOutputStream(storeFile)) {
|
||||
IOUtils.copy(response.getEntity().getContent(), fos);
|
||||
ByteArrayOutputStream bufferOutputStream = new ByteArrayOutputStream();
|
||||
try(FileOutputStream fileOutputStream = new FileOutputStream(storeFile)) {
|
||||
IOUtils.copy(response.getEntity().getContent(), bufferOutputStream);
|
||||
ByteArrayInputStream bufferInputStream = new ByteArrayInputStream(bufferOutputStream.toByteArray());
|
||||
CacheStoreCentral.ImageChecksum imageChecksum = CacheStoreCentral.ImageChecksum
|
||||
.buildImageChecksumFromStream(
|
||||
event.getIllustId(),
|
||||
event.getPageIndex(),
|
||||
event.getStoreFile().getName(),
|
||||
bufferInputStream
|
||||
);
|
||||
bufferInputStream.reset();
|
||||
IOUtils.copy(bufferInputStream, fileOutputStream);
|
||||
CacheStoreCentral.setImageChecksum(imageChecksum);
|
||||
} catch (IOException e) {
|
||||
log.error("下载图片时发生异常", e);
|
||||
throw e;
|
||||
|
@ -12,13 +12,16 @@ public class ImageCacheObject implements EventObject {
|
||||
|
||||
private final int illustId;
|
||||
|
||||
private final int pageIndex;
|
||||
|
||||
private final String downloadLink;
|
||||
|
||||
private final File storeFile;
|
||||
|
||||
public ImageCacheObject(Map<String, File> imageCache, int illustId, String downloadLink, File storeFile) {
|
||||
public ImageCacheObject(Map<String, File> imageCache, int illustId, int pageIndex, String downloadLink, File storeFile) {
|
||||
this.imageCache = imageCache;
|
||||
this.illustId = illustId;
|
||||
this.pageIndex = pageIndex;
|
||||
this.downloadLink = downloadLink;
|
||||
this.storeFile = storeFile;
|
||||
}
|
||||
@ -39,12 +42,17 @@ public class ImageCacheObject implements EventObject {
|
||||
return illustId;
|
||||
}
|
||||
|
||||
public int getPageIndex() {
|
||||
return pageIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ImageCacheObject that = (ImageCacheObject) o;
|
||||
return illustId == that.illustId &&
|
||||
pageIndex == that.pageIndex &&
|
||||
Objects.equals(imageCache, that.imageCache) &&
|
||||
Objects.equals(downloadLink, that.downloadLink) &&
|
||||
Objects.equals(storeFile, that.storeFile);
|
||||
@ -52,13 +60,15 @@ public class ImageCacheObject implements EventObject {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(imageCache, illustId, downloadLink, storeFile);
|
||||
return Objects.hash(imageCache, illustId, pageIndex, downloadLink, storeFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImageCacheObject@" + Integer.toHexString(hashCode()) + "{" +
|
||||
"illustId=" + illustId +
|
||||
return "ImageCacheObject{" +
|
||||
"imageCache=" + imageCache +
|
||||
", illustId=" + illustId +
|
||||
", pageIndex=" + pageIndex +
|
||||
", downloadLink='" + downloadLink + '\'' +
|
||||
", storeFile=" + storeFile +
|
||||
'}';
|
||||
|
Loading…
Reference in New Issue
Block a user