如何在Java Web应用程序中使UTF-8正常工作?

375
我需要让我的Java Web应用程序(使用servlets和JSP,没有使用框架)支持UTF-8,以便正常的芬兰文本如äöå等和特殊情况下像ЦжФ这样的西里尔字母都能够被支持。
我的设置如下:
  • 开发环境:Windows XP
  • 生产环境:Debian
数据库使用:MySQL 5.x
用户主要使用Firefox2,但也使用Opera 9.x、FF3、IE7和Google Chrome来访问网站。
如何实现这一点?

请参见https://dev59.com/HXVC5IYBdhLWcg3w4Vf6。 - Raedwald
14个回答

563

作为该网站FAQ的回答者,鼓励这样做。以下方法对我有效:

大多数字符(如äåö)通常不会有问题,因为浏览器和Tomcat/Java用于Web应用程序的默认字符集是Latin1即ISO-8859-1,可以“理解”这些字符。

要在Java+Tomcat+Linux/Windows+Mysql环境下使用UTF-8,需要进行以下配置:

配置Tomcat的server.xml

必须配置连接器使用UTF-8来编码url(GET请求)参数:

<Connector port="8080" maxHttpHeaderSize="8192"
 maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
 enableLookups="false" redirectPort="8443" acceptCount="100"
 connectionTimeout="20000" disableUploadTimeout="true" 
 compression="on" 
 compressionMinSize="128" 
 noCompressionUserAgents="gozilla, traviata" 
 compressableMimeType="text/html,text/xml,text/plain,text/css,text/ javascript,application/x-javascript,application/javascript"
 URIEncoding="UTF-8"
/>

上面示例中关键部分是URIEncoding="UTF-8"。这保证了Tomcat将所有传入的GET参数作为UTF-8编码处理。因此,当用户在浏览器地址栏中输入以下内容时:
 https://localhost:8443/ID/Users?action=search&name=*ж*

字符"ж"在处理时被视为UTF-8,并且通常由浏览器编码为"%D0%B6"(甚至在到达服务器之前就被编码了)。

POST请求不受此影响。

CharsetFilter

然后就需要强制java web应用程序将所有请求和响应都视为UTF-8编码。这需要我们定义一个字符集过滤器,如下所示:

package fi.foo.filters;

import javax.servlet.*;
import java.io.IOException;

public class CharsetFilter implements Filter {

    private String encoding;

    public void init(FilterConfig config) throws ServletException {
        encoding = config.getInitParameter("requestEncoding");
        if (encoding == null) encoding = "UTF-8";
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain next)
            throws IOException, ServletException {
        // Respect the client-specified character encoding
        // (see HTTP specification section 3.4.1)
        if (null == request.getCharacterEncoding()) {
            request.setCharacterEncoding(encoding);
        }

        // Set the default response content type and encoding
        response.setContentType("text/html; charset=UTF-8");
        response.setCharacterEncoding("UTF-8");

        next.doFilter(request, response);
    }

    public void destroy() {
    }
}

这个过滤器确保如果浏览器没有设置请求中使用的编码方式,则将其设置为UTF-8。
此过滤器执行的另一项任务是设置默认响应编码,即返回的html /其他内容的编码方式。另一种方法是在应用程序的每个控制器中设置响应编码等。
必须将此过滤器添加到webapp的web.xml或部署描述符中:
 <!--CharsetFilter start--> 

  <filter>
    <filter-name>CharsetFilter</filter-name>
    <filter-class>fi.foo.filters.CharsetFilter</filter-class>
      <init-param>
        <param-name>requestEncoding</param-name>
        <param-value>UTF-8</param-value>
      </init-param>
  </filter>

  <filter-mapping>
    <filter-name>CharsetFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

制作此过滤器的说明可以在Tomcat Wiki (http://wiki.apache.org/tomcat/Tomcat/UTF-8)中找到。

JSP页面编码

在您的web.xml中添加以下内容:

<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <page-encoding>UTF-8</page-encoding>
    </jsp-property-group>
</jsp-config>

或者,Web应用程序的所有JSP页面都需要在顶部具有以下内容:

 <%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>

如果使用了不同的JSP片段来进行布局,则需要在所有片段中使用此功能。

HTML-meta标签

JSP页面编码告诉JVM以正确的编码处理JSP页面中的字符。然后,需要告诉浏览器html页面所使用的编码:

这可以通过在Web应用程序生成的每个xhtml页面顶部执行以下操作来完成:

   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fi">
   <head>
   <meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
   ...

JDBC连接

在使用数据库时,必须定义连接使用UTF-8编码。这可以在context.xml或任何定义JDBC连接的地方进行如下设置:

      <Resource name="jdbc/AppDB" 
        auth="Container"
        type="javax.sql.DataSource"
        maxActive="20" maxIdle="10" maxWait="10000"
        username="foo"
        password="bar"
        driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/      ID_development?useEncoding=true&amp;characterEncoding=UTF-8"
    />

MySQL数据库和表

使用的数据库必须使用UTF-8编码。通过以下方式创建数据库来实现:

   CREATE DATABASE `ID_development` 
   /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_swedish_ci */;

然后,所有的表格也需要是UTF-8格式的:
   CREATE TABLE  `Users` (
    `id` int(10) unsigned NOT NULL auto_increment,
    `name` varchar(30) collate utf8_swedish_ci default NULL
    PRIMARY KEY  (`id`)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci ROW_FORMAT=DYNAMIC;

重点是CHARSET=utf8

MySQL服务器配置

MySQL服务器也需要进行配置。通常在Windows中,这是通过修改my.ini文件,在Linux中则是通过配置my.cnf文件来完成的。 在这些文件中,应该定义所有连接到服务器的客户端使用utf8作为默认字符集,并且服务器使用的默认字符集也是utf8。

   [client]
   port=3306
   default-character-set=utf8

   [mysql]
   default-character-set=utf8

Mysql存储过程和函数

这些也需要定义字符集。例如:

   DELIMITER $$

   DROP FUNCTION IF EXISTS `pathToNode` $$
   CREATE FUNCTION `pathToNode` (ryhma_id INT) RETURNS TEXT CHARACTER SET utf8
   READS SQL DATA
   BEGIN

    DECLARE path VARCHAR(255) CHARACTER SET utf8;

   SET path = NULL;

   ...

   RETURN path;

   END $$

   DELIMITER ;

GET请求:latin1和UTF-8

如果在Tomcat的server.xml中定义了GET请求参数以UTF-8编码,以下GET请求将被正确处理:

   https://localhost:8443/ID/Users?action=search&name=Petteri
   https://localhost:8443/ID/Users?action=search&name=ж

由于ASCII字符在latin1和UTF-8中的编码方式相同,因此字符串“Petteri”被正确处理。
拉丁字母表中根本无法理解西里尔字母“ж”。由于Tomcat被指示将请求参数作为UTF-8处理,它会将该字符正确编码为%D0%B6
如果浏览器被指示以UTF-8编码读取页面(使用请求标头和HTML元标记),至少Firefox 2/3和其他来自此时期的浏览器都会将字符本身编码为%D0%B6
最终结果是所有名为“Petteri”的用户都被找到,所有名为“ж”的用户也都被找到。
但是,对于äåö呢?
HTTP规范定义默认情况下URL以latin1编码。这导致firefox2、firefox3等编码以下内容:
    https://localhost:8443/ID/Users?action=search&name=*Päivi*

转换为编码版本

    https://localhost:8443/ID/Users?action=search&name=*P%E4ivi*

在latin1中,字符ä被编码为%E4尽管页面/请求/所有内容都定义为使用UTF-8。ä的UTF-8编码版本为%C3%A4
结果是,Web应用程序几乎无法正确处理GET请求中的请求参数,因为一些字符以latin1编码,而另一些字符以UTF-8编码。 注意:如果页面被定义为UTF-8,则POST请求可以正常工作,因为浏览器会完全以UTF-8编码表单中的所有请求参数

需要阅读的内容

非常感谢以下作者为我的问题提供答案:

  • http://tagunov.tripod.com/i18n/i18n.html - 关于国际化和本地化的文章
  • http://wiki.apache.org/tomcat/Tomcat/UTF-8 - Tomcat中关于UTF-8编码的问题
  • http://java.sun.com/developer/technicalArticles/Intl/HTTPCharset/ - Java中关于HTTP字符集的文章
  • http://dev.mysql.com/doc/refman/5.0/en/charset-syntax.html - MySQL中关于字符集的语法
  • http://cagan327.blogspot.com/2006/05/utf-8-encoding-fix-tomcat-jsp-etc.html - 修复Tomcat JSP等UTF-8编码问题的方法
  • http://cagan327.blogspot.com/2006/05/utf-8-encoding-fix-for-mysql-tomcat.html - 修复MySQL和Tomcat中UTF-8编码问题的方法
  • http://jeppesn.dk/utf-8.html - UTF-8编码的介绍和使用方法
  • http://www.nabble.com/request-parameters-mishandle-utf-8-encoding-td18720039.html - 处理请求参数UTF-8编码错误的问题
  • http://www.utoronto.ca/webdocs/HTMLdocs/NewHTML/iso_table.html - HTML中ISO字符集表
  • http://www.utf8-chartable.de/ - UTF-8编码表

重要提示

支持使用3字节UTF-8字符来支持基本多语言平面。如果您需要使用超出此范围的字符(某些字母需要使用超过3字节的UTF-8),那么您需要使用VARBINARY列类型或使用utf8mb4字符集(需要MySQL 5.5.3或更高版本)。只需注意,在MySQL中使用utf8字符集并不总是有效。

Apache下的Tomcat

还有一件事,如果您正在使用Apache + Tomcat + mod_JK连接器,则还需要进行以下更改:

  1. 将URIEncoding="UTF-8"添加到tomcat server.xml文件的8009连接器中,它由mod_JK连接器使用。 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" URIEncoding="UTF-8"/>
  2. 进入您的apache文件夹,即/etc/httpd/conf,并在httpd.conf文件中添加AddDefaultCharset utf-8注意:首先检查它是否存在。如果存在,您可以使用此行更新它。您也可以在底部添加此行。

18
两条评论:1)在HTML-meta标签中,你包含了一个xml声明。请移除它,因为这会触发浏览器进入怪异模式,这是不想要的。此外,实际上JSP pageEncoding已经隐含地完成了HTML元标记,所以你甚至可以将其省略。2)在MySQL数据库和表中,你使用了utf8_swedish_si,应该使用utf8_unicode_ci。你甚至可以省略排序规则(collation),只需使用CHARACTER SET utf8即可。 - BalusC
4
如果你正在使用Apache + Tomcat + mod_JK连接器,则需要进行以下更改:
  1. 在Tomcat的server.xml文件中为端口8009的连接器添加URIEncoding="UTF-8",这是由mod_JK连接器使用的。<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" URIEncoding="UTF-8"/>
  2. 进入你的Apache文件夹,例如/etc/httpd/conf,并在' httpd.conf '文件中添加 AddDefaultCharset utf-8注意: 首先检查它是否存在。如果存在,则可以使用此行进行更新。您也可以将此行添加到底部。
- Vijay Shegokar
这个过滤器必须是web.xml中的第一个过滤器。 - olyanren
我按照所有步骤进行了操作,但还是不起作用。tomcat+spring+jsp表单。 - Dariush Jafari
1
关于Tomcat的注意事项:从Tomcat 8开始,除非您使用严格的规范兼容性(在这种情况下它默认为ISO-8859但仍然可以被覆盖),否则<Connector>上的默认URIEncoding现在是UTF-8 - Christopher Schultz
显示剩余15条评论

14

我认为你在自己的回答中已经很好地总结了它。

在从端到端进行UTF-8转换的过程中,您可能还想确保java本身正在使用UTF-8。将-Dfile.encoding=utf-8用作JVM的参数(可以在catalina.bat中配置)。


这对我很有帮助,我按照提到的一切做了,但JVM编码是windows-1250,当我改为UTF-8后,它就完美地工作了。 - coding_idiot
2
请问您在Catalina.bat文件中添加在哪里? - Noah

12

除了kosoant的答案,如果你使用的是Spring,而不是编写自己的Servlet过滤器,你可以使用他们提供的org.springframework.web.filter.CharacterEncodingFilter类,在web.xml中进行如下配置:

 <filter>
    <filter-name>encoding-filter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
       <param-name>encoding</param-name>
       <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
       <param-name>forceEncoding</param-name>
       <param-value>FALSE</param-value>
    </init-param>
 </filter>
 <filter-mapping>
    <filter-name>encoding-filter</filter-name>
    <url-pattern>/*</url-pattern>
 </filter-mapping>

1
这个过滤器必须是 web.xml 中第一个过滤器。 - olyanren

2

我还想从这里添加一部分内容,解决了我的UTF问题:

runtime.encoding=<encoding>

1

这是关于在使用Java访问MySql表时,需要进行希腊编码的设置:

请在您的JBoss连接池(mysql-ds.xml)中使用以下连接设置:

<connection-url>jdbc:mysql://192.168.10.123:3308/mydatabase</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>nts</user-name>
<password>xaxaxa!</password>
<connection-property name="useUnicode">true</connection-property>
<connection-property name="characterEncoding">greek</connection-property>

如果您不想将其放入JNDI连接池中,可以像下一行所示那样将其配置为JDBC-url:

jdbc:mysql://192.168.10.123:3308/mydatabase?characterEncoding=greek

对于我和尼克来说,这样我们就不会再忘记它并浪费时间了.....


5
我仍然更喜欢使用UTF-8而不是希腊语(并将你现有的希腊语数据转换为UTF-8),以便你的应用程序可以准备好征服全球。 - BalusC

1
很详细的答案。想补充一点,这对其他人来说肯定会很有帮助,可以看到URL中的UTF-8编码在实际中的应用。
请按照以下步骤在火狐浏览器上启用URL中的UTF-8编码:
1. 在地址栏中输入 "about:config"。 2. 使用过滤器输入类型搜索 "network.standard-url.encode-query-utf8" 属性。 3. 该属性默认为false,请将其改为TRUE。 4. 重新启动浏览器。
在IE6/7/8和Chrome中,默认情况下支持URL中的UTF-8编码。

1

之前的回答对我的问题没有帮助。问题只出现在生产环境中,使用tomcat和apache mod_proxy_ajp。发帖内容会因为什么原因丢失非ASCII字符。 最终问题出在JVM默认字符集(默认安装为US-ASCII:Charset dfset = Charset.defaultCharset();) 因此,解决方案是通过修改器运行tomcat服务器,以UTF-8作为默认字符集来运行JVM:

JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8" 

(在catalina.sh中添加此行并重新启动tomcat服务)

也许您还需要更改Linux系统变量(编辑~/.bashrc和~/.profile以进行永久更改,请参见https://perlgeek.de/en/article/set-up-a-clean-utf8-environment

export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8

export LANGUAGE=en_US.UTF-8


0

我遇到了类似的问题,但是这是在我使用Apache Commons压缩文件时出现在文件名中的。 所以,我用这个命令解决了它:

convmv --notest -f cp1252 -t utf8 * -r

对我来说它非常有效。希望能帮到任何人 ;)


0
有时候你可以通过MySQL管理员向导来解决问题。在启动变量 > 高级设置中,设置默认字符集为utf8。
也许这个配置需要重新启动MySQL。

0

关于@kosoant答案中提到的CharsetFilter...

在Tomcat的web.xml(位于conf/web.xml)中有一个内置的Filter。该过滤器的名称为setCharacterEncodingFilter,默认情况下被注释掉了。您可以取消注释它(请记得同时取消注释其filter-mapping)。

此外,在您的web.xml中不需要设置jsp-config(我已经为Tomcat 7+测试过了)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接