All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.hutool.core.codec.PercentCodec Maven / Gradle / Ivy

There is a newer version: 5.8.33
Show newest version
package cn.hutool.core.codec;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.BitSet;

/**
 * 百分号编码(Percent-encoding), 也称作URL编码(URL encoding)。
* 百分号编码可用于URI的编码,也可以用于"application/x-www-form-urlencoded"的MIME准备数据。 * *

* 百分号编码会对 URI 中不允许出现的字符或者其他特殊情况的允许的字符进行编码,对于被编码的字符,最终会转为以百分号"%“开头,后面跟着两位16进制数值的形式。 * 举个例子,空格符(SP)是不允许的字符,在 ASCII 码对应的二进制值是"00100000”,最终转为"%20"。 *

*

* 对于不同场景应遵循不同规范: * *

    *
  • URI:遵循RFC 3986保留字规范
  • *
  • application/x-www-form-urlencoded,遵循W3C HTML Form content types规范,如空格须转+
  • *
* * @author looly * @since 5.7.16 */ public class PercentCodec implements Serializable { private static final long serialVersionUID = 1L; /** * 从已知PercentCodec创建PercentCodec,会复制给定PercentCodec的安全字符 * * @param codec PercentCodec * @return PercentCodec */ public static PercentCodec of(PercentCodec codec) { return new PercentCodec((BitSet) codec.safeCharacters.clone()); } /** * 创建PercentCodec,使用指定字符串中的字符作为安全字符 * * @param chars 安全字符合集 * @return PercentCodec */ public static PercentCodec of(CharSequence chars) { Assert.notNull(chars, "chars must not be null"); final PercentCodec codec = new PercentCodec(); final int length = chars.length(); for (int i = 0; i < length; i++) { codec.addSafe(chars.charAt(i)); } return codec; } /** * 存放安全编码 */ private final BitSet safeCharacters; /** * 是否编码空格为+
* 如果为{@code true},则将空格编码为"+",此项只在"application/x-www-form-urlencoded"中使用
* 如果为{@code false},则空格编码为"%20",此项一般用于URL的Query部分(RFC3986规范) */ private boolean encodeSpaceAsPlus = false; /** * 构造
* [a-zA-Z0-9]默认不被编码 */ public PercentCodec() { this(new BitSet(256)); } /** * 构造 * * @param safeCharacters 安全字符,安全字符不被编码 */ public PercentCodec(BitSet safeCharacters) { this.safeCharacters = safeCharacters; } /** * 增加安全字符
* 安全字符不被编码 * * @param c 字符 * @return this */ public PercentCodec addSafe(char c) { safeCharacters.set(c); return this; } /** * 移除安全字符
* 安全字符不被编码 * * @param c 字符 * @return this */ public PercentCodec removeSafe(char c) { safeCharacters.clear(c); return this; } /** * 增加安全字符到挡墙的PercentCodec * * @param codec PercentCodec * @return this */ public PercentCodec or(PercentCodec codec) { this.safeCharacters.or(codec.safeCharacters); return this; } /** * 组合当前PercentCodec和指定PercentCodec为一个新的PercentCodec,安全字符为并集 * * @param codec PercentCodec * @return 新的PercentCodec */ public PercentCodec orNew(PercentCodec codec) { return of(this).or(codec); } /** * 是否将空格编码为+
* 如果为{@code true},则将空格编码为"+",此项只在"application/x-www-form-urlencoded"中使用
* 如果为{@code false},则空格编码为"%20",此项一般用于URL的Query部分(RFC3986规范) * * @param encodeSpaceAsPlus 是否将空格编码为+ * @return this */ public PercentCodec setEncodeSpaceAsPlus(boolean encodeSpaceAsPlus) { this.encodeSpaceAsPlus = encodeSpaceAsPlus; return this; } /** * 将URL中的字符串编码为%形式 * * @param path 需要编码的字符串 * @param charset 编码, {@code null}返回原字符串,表示不编码 * @param customSafeChar 自定义安全字符 * @return 编码后的字符串 */ public String encode(CharSequence path, Charset charset, char... customSafeChar) { if (null == charset || StrUtil.isEmpty(path)) { return StrUtil.str(path); } final StringBuilder rewrittenPath = new StringBuilder(path.length()); final ByteArrayOutputStream buf = new ByteArrayOutputStream(); final OutputStreamWriter writer = new OutputStreamWriter(buf, charset); char c; for (int i = 0; i < path.length(); i++) { c = path.charAt(i); if (safeCharacters.get(c) || ArrayUtil.contains(customSafeChar, c)) { rewrittenPath.append(c); } else if (encodeSpaceAsPlus && c == CharUtil.SPACE) { // 对于空格单独处理 rewrittenPath.append('+'); } else { // convert to external encoding before hex conversion try { writer.write(c); writer.flush(); } catch (IOException e) { buf.reset(); continue; } // 兼容双字节的Unicode符处理(如部分emoji) byte[] ba = buf.toByteArray(); for (byte toEncode : ba) { // Converting each byte in the buffer rewrittenPath.append('%'); HexUtil.appendHex(rewrittenPath, toEncode, false); } buf.reset(); } } return rewrittenPath.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy