1
0
mirror of synced 2025-11-06 09:50:35 +08:00

!2 解决自定义字体产生临时文件消耗硬盘的问题,以及解决总分操作系统下无法读到字体文件的问题

Merge pull request !2 from hellozrh/master
This commit is contained in:
ele-admin
2022-05-07 03:40:09 +00:00
committed by Gitee
10 changed files with 248 additions and 23 deletions

3
.gitignore vendored
View File

@@ -22,4 +22,5 @@
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/.nb-gradle/
/classes/

2
.idea/compiler.xml generated
View File

@@ -10,7 +10,7 @@
</profile>
</annotationProcessing>
<bytecodeTargetLevel>
<module name="EasyCaptcha" target="6" />
<module name="EasyCaptcha" target="8" />
</bytecodeTargetLevel>
</component>
</project>

View File

@@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Maven: junit:junit:4.7">
<CLASSES>
<root url="jar://$USER_HOME$/.m2/repository/junit/junit/4.7/junit-4.7.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$USER_HOME$/.m2/repository/junit/junit/4.7/junit-4.7-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$USER_HOME$/.m2/repository/junit/junit/4.7/junit-4.7-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -1,16 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6">
<component name="FacetManager">
<facet type="web" name="Web">
<configuration>
<descriptors>
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/web/WEB-INF/web.xml" />
</descriptors>
<webroots>
<root url="file://$MODULE_DIR$/web" relative="/" />
</webroots>
<sourceRoots>
<root url="file://$MODULE_DIR$/src/main/java" />
<root url="file://$MODULE_DIR$/src/main/resources" />
</sourceRoots>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="PROVIDED" name="Maven: javax.servlet:javax.servlet-api:3.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.7" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
</component>
</module>

View File

@@ -1,12 +1,11 @@
package com.wf.captcha.base;
import com.wf.captcha.utils.FontsUtil;
import java.awt.*;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.QuadCurve2D;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.*;
import java.util.Base64;
/**
@@ -275,7 +274,7 @@ public abstract class Captcha extends Randoms {
}
public void setFont(int font, int style, float size) throws IOException, FontFormatException {
this.font = Font.createFont(Font.TRUETYPE_FONT, new File(getClass().getResource("/" + FONT_NAMES[font]).getFile())).deriveFont(style, size);
this.font = FontsUtil.getFont(FONT_NAMES[font], style, size);
}
public int getLen() {

View File

@@ -1,5 +1,6 @@
package com.wf.captcha.servlet;
import java.awt.*;
import java.io.IOException;
import javax.servlet.ServletException;
@@ -7,6 +8,9 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.wf.captcha.GifCaptcha;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import com.wf.captcha.utils.CaptchaUtil;
/**
@@ -18,7 +22,16 @@ public class CaptchaServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
CaptchaUtil.out(request, response);
// SpecCaptcha captcha = new SpecCaptcha(130, 48, 6);
GifCaptcha captcha = new GifCaptcha(130, 48, 6);
// 设置内置字体
try {
captcha.setFont(Captcha.FONT_10);
} catch (FontFormatException e) {
e.printStackTrace();
}
CaptchaUtil.out(captcha, request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)

View File

@@ -0,0 +1,107 @@
package com.wf.captcha.utils;
import java.io.*;
import java.util.Objects;
/**
* 文件操作工具类此类源码从org.apache.commons.io.FileUtils中复制
*
* @author zrh 455741807@qq.com
* @date 2022-05-07
*/
public class FileUtil {
public static final int DEFAULT_BUFFER_SIZE = 8192;
public static final int EOF = -1;
/**
* 文件流复制
* @param inputStream
* @param file
* @throws IOException
*/
public static void copyToFile(final InputStream inputStream, final File file) throws IOException {
try (OutputStream out = openOutputStream(file)) {
copy(inputStream, out);
}
}
public static FileOutputStream openOutputStream(final File file) throws IOException {
return openOutputStream(file, false);
}
public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException {
Objects.requireNonNull(file, "file");
if (file.exists()) {
requireFile(file, "file");
requireCanWrite(file, "file");
} else {
createParentDirectories(file);
}
return new FileOutputStream(file, append);
}
private static File requireFile(final File file, final String name) {
Objects.requireNonNull(file, name);
if (!file.isFile()) {
throw new IllegalArgumentException("Parameter '" + name + "' is not a file: " + file);
}
return file;
}
private static void requireCanWrite(final File file, final String name) {
Objects.requireNonNull(file, "file");
if (!file.canWrite()) {
throw new IllegalArgumentException("File parameter '" + name + " is not writable: '" + file + "'");
}
}
public static File createParentDirectories(final File file) throws IOException {
return mkdirs(getParentFile(file));
}
private static File mkdirs(final File directory) throws IOException {
if ((directory != null) && (!directory.mkdirs() && !directory.isDirectory())) {
throw new IOException("Cannot create directory '" + directory + "'.");
}
return directory;
}
private static File getParentFile(final File file) {
return file == null ? null : file.getParentFile();
}
public static int copy(final InputStream inputStream, final OutputStream outputStream) throws IOException {
final long count = copyLarge(inputStream, outputStream);
if (count > Integer.MAX_VALUE) {
return EOF;
}
return (int) count;
}
public static long copyLarge(final InputStream inputStream, final OutputStream outputStream)
throws IOException {
return copy(inputStream, outputStream, DEFAULT_BUFFER_SIZE);
}
public static long copy(final InputStream inputStream, final OutputStream outputStream, final int bufferSize)
throws IOException {
return copyLarge(inputStream, outputStream, byteArray(bufferSize));
}
public static long copyLarge(final InputStream inputStream, final OutputStream outputStream, final byte[] buffer)
throws IOException {
Objects.requireNonNull(inputStream, "inputStream");
Objects.requireNonNull(outputStream, "outputStream");
long count = 0;
int n;
while (EOF != (n = inputStream.read(buffer))) {
outputStream.write(buffer, 0, n);
count += n;
}
return count;
}
public static byte[] byteArray(final int size) {
return new byte[size];
}
}

View File

@@ -0,0 +1,75 @@
package com.wf.captcha.utils;
import sun.security.action.GetPropertyAction;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.security.AccessController.doPrivileged;
/**
* 解决自定义字体读取时,产生.tmp临时文件耗磁盘的问题。
*
* 解决思路:
* Font类的createFont有个重载方法>java.awt.Font#createFont(int, java.io.File),
* 不产生临时文件获取字体代码实现
* <code>
* URL url = FontLoader.class.getResource("font/SourceHanSansCN-Regular.otf");
* String pathString = url.getFile();
* Font selfFont = Font.createFont(Font.TRUETYPE_FONT, new File(pathString));
* </code>
* 上面的解决方案会导致另一个问题字体文件在生产环境是在jar包里部分操作系统环境下直接读取读取不到只能通过流的方式获取。
*
* 因此本方案采用的办法是把jar包中的字体文件复制到java.io.tmpdir临时文件夹中
* ,再采用<code>java.awt.Font#createFont(int, java.io.File)</code>的方式产生字体既解决了临时文件tmp消耗磁盘的问题也解决了
* 部分操作系统下读不到文件的问题。
*
* @author zrh 455741807@qq.com
* @date 2022-05-07
*
*/
public class FontsUtil {
private static final Path tmpdir = Paths.get(doPrivileged(new GetPropertyAction("java.io.tmpdir")));
/**
* 手动复制字体文件到临时目录. 调用传文件的构造方法创建字体
* @param fontName 字体文件名称
* @return
*/
public static Font getFont(String fontName, int style, float size) {
Font font = null;
File tempFontFile = new File(tmpdir.toUri().getPath() + fontName);
if(!tempFontFile.exists()){
//临时文件不存在
copyTempFontFile(fontName, tempFontFile);
}
if(tempFontFile.exists()) {
try {
font = Font.createFont(Font.TRUETYPE_FONT, tempFontFile).deriveFont(style, size);;
} catch (FontFormatException | IOException e) {
e.printStackTrace();
tempFontFile.delete();
}
}
return font;
}
/**
* 复制字体文件到临时文件目录
* @param fontName
* @param tempFontFile
*/
private static synchronized void copyTempFontFile(String fontName, File tempFontFile){
try(InputStream is = FontsUtil.class.getResourceAsStream("/" + fontName)){
FileUtil.copyToFile(is, tempFontFile);
} catch (IOException e) {
e.printStackTrace();
}
}
}

15
web/WEB-INF/web.xml Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 图形验证码servlet -->
<servlet>
<servlet-name>CaptchaServlet</servlet-name>
<servlet-class>com.wf.captcha.servlet.CaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CaptchaServlet</servlet-name>
<url-pattern>/captcha</url-pattern>
</servlet-mapping>
</web-app>

10
web/index.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>验证码测试</title>
</head>
<body>
<img src="/captcha" width="130px" height="48px" />
</body>
</html>