Decode a URL component only when it is a URL component
A downloaded file can have a name such as report%20final.json. That spelling alone does not prove that a space was encoded. It may be the literal name chosen by its author. Before decoding anything, establish whether the input is a URL component, a form query value, or an ordinary filename.
Three inputs that look similar
In JavaScript, decodeURIComponent decodes percent-encoded UTF-8 sequences. It does not treat a plus sign as a space. A form-style query string has different rules: URLSearchParams interprets a plus sign as a space while parsing the query.
decodeURIComponent("report%20final.json") // "report final.json"
decodeURIComponent("a+b") // "a+b"
new URLSearchParams("name=a+b").get("name") // "a b"
new URLSearchParams("name=a%2Bb").get("name") // "a+b"
The distinction affects real debugging. If a form field supplies a literal plus sign as %2B, parsing it once gives the intended plus sign. Replacing every plus sign afterward would corrupt it. Conversely, applying decodeURIComponent directly to an entire unparsed query does not implement form parsing.
Parse structure first
Use the URL parser to separate path, query, and fragment. Apply a component decoder only to a value whose encoding contract calls for it. This synthetic example preserves the fact that the slash belongs inside a query value:
const u = new URL("https://example.invalid/view?file=a%2Fb.txt");
u.searchParams.get("file"); // "a/b.txt"; already decoded by the parser
Do not decode the entire URL before parsing it. Encoded characters can become structural delimiters. Also, decoding a query value does not establish that it is a safe local path. File access needs separate validation and a permitted base directory; the example above performs no file operation.
Decode once, and handle failure
Decoding twice changes the meaning of deliberately encoded percent signs:
decodeURIComponent("100%2525") // "100%25"
decodeURIComponent("100%25") // "100%"
The first string may intentionally represent the literal characters 100%25. A second pass is not a general repair. Record which boundary performs decoding and keep the original input for comparison.
Malformed escapes and invalid UTF-8 can throw URIError. A trailing percent sign is one simple example. Treat failure as a result to report, not a reason to discard characters until the string looks plausible.
function decodeComponent(value) {
try {
return { ok: true, value: decodeURIComponent(value) };
} catch (error) {
if (!(error instanceof URIError)) throw error;
return { ok: false, reason: "Invalid percent-encoded UTF-8 component" };
}
}
Keep export diagnostics separate
For a downloaded chat archive, retain the original filenames and directory structure. Do not rename every percent-containing file as a troubleshooting shortcut: references in the exported HTML or JSON may depend on the exact original spelling. Test a copy and inspect the actual referencing format first.
Netzhi publishes Chinese software guides, including a Telegram export walkthrough. This companion note addresses URL parsing after a download; it does not claim that decoding filenames restores missing messages or attachments.
Published by 网知 / Netzhi. All examples use synthetic data and make no network requests.