從跟蹤代碼來看,content-disposition存放的是http response的raw header。直到在HttpContentDisposition類的filename_成員才會存放轉換了的編碼。
這個轉換編碼的猜測流程:asc,utf,有指定編碼,按指定;否則按系統的字符集。
參考:
https://blog.csdn.net/lc11535/article/details/100013653
https://blog.csdn.net/weixin_34366546/article/details/85839548?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
在線字符編碼轉換:http://www.mytju.com/classcode/tools/encode_gb2312.asp
https://blog.csdn.net/iteye_14084/article/details/81554791
https://www.cnblogs.com/batsing/p/charset.html
https://zhidao.baidu.com/question/89668249.html
比如:“中文” 字符得編碼:
Unicode中為:4E2D 6587
GBK(gb2312 gb18030)中為:D6D0 CEC4
這是在內存中存放形式。chrome內部統一用Unicode在內存存放,所以會有一張gbk到unicode得對照表,將“中”的 gbk D6D0 轉換為 unicode的4E2D。
當需要把“中文”這兩字保存到文件,或者網絡傳輸時,直接保存需要兩個字節,這樣會浪費保存英文的存儲空間,因為英文只需要一個字節。所以這時就有個編碼的需求。一般都是用utf8。英文直接還是用一個字節;中文就要用3個字節。utf8,utf16,utf32都是對unicode的編碼存儲。
“中文”的utf8存成文件為:
而“中文”的GBK存儲時,直接就是按編碼存儲。
gb2312:
規定:一個小於127的字符的意義與原來相同,但兩個大於127的字符連在一起時,就表示一個漢字,前面的一個字節(他稱之為高字節)從0xA1用到0xF7,后面一個字節(低字節)從0xA1到0xFE,這樣我們就可以組合出大約7000多個簡體漢字了
gbk:只要高位是1開始,即大於127;不再管地位。這樣增加了2萬漢字。
GB18030:加入了少數民族的字。
Unicode出現:所有字符都占兩位
wchar_t * p = L"Hello!" ;//占10個字節
沒有一種簡單的算術方法可以把文本內容從UNICODE編碼和另一種編碼進行轉換,這種轉換必須通過查表來進行。如unicode 到gbk。具體的符號對應表,可以查詢unicode.org,或者專門的漢字對應表。
UNICODE("Universal Multiple-Octet Coded Character Set",簡稱 UCS, 俗稱 "UNICODE"。) 是用兩個字節來表示為一個字符,組合出65535不同的字符,這大概已經可以覆蓋世界上所有文化的符號。
如果還不夠也沒有關系,ISO已經准備了UCS-4方案,說簡單了就是四個字節來表示一個字符,這樣我們就可以組合出21億個不同的字符出來(最高位有其他用途)。
面向網絡傳輸規范出現:UTF(UCS Transfer Format)顧名思義,UTF8就是每次8個位傳輸數據,而UTF16就是每次16個位,只不過為了傳輸時的可靠性,從UNICODE到UTF時並不是直接的對應,而是要過一些算法和規則來轉換。重復一遍,這里的關系是,UTF-8是Unicode的實現方式之一。
INTEL低位先發送"FEFF",否則高位,utf的文件頭都有這個標志。
打開文件時 EF BB BF, 表示UTF-8
Unicode | UTF-8 |
0000 - 007F | 0xxxxxxx |
0080 - 07FF | 110xxxxx 10xxxxxx |
0800 - FFFF |
1110xxxx 10xxxxxx 10xxxxxx |
記事本的編碼默認是ANSI, 如果你在ANSI的編碼輸入漢字,那么他實際就是GB系列的編碼方式,在這種編碼下,"聯通"的內碼是:
c1 1100 0001
aa 1010 1010
cd 1100 1101
a8 1010 1000
windows的記事本保存時選擇編碼格式:
里面有四個選項:ANSI,Unicode,Unicode big endian 和 UTF-8。
1)ANSI是默認的編碼方式。對於英文文件是ASCII編碼,對於簡體中文文件是GB2312編碼(只針對Windows簡體中文版,如果是繁體中文版會采用Big5碼)。
2)Unicode編碼指的是UCS-2編碼方式,即直接用兩個字節存入字符的Unicode碼。這個選項用的little endian格式。
3)Unicode big endian編碼與上一個選項相對應。我在下一節會解釋little endian和big endian的涵義。
4)UTF-8編碼,也就是上一節談到的編碼方法。
選擇完”編碼方式“后,點擊”保存“按鈕,文件的編碼方式就立刻轉換好了。
Unicode碼可以采用UCS-2格式直接存儲。以漢字”嚴“為例,Unicode碼是4E25,需要用兩個字節存儲,一個字節是4E,另一個字節是25。存儲的時候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。
Unicode規范中定義,每一個文件的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做”零寬度非換行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。這正好是兩個字節,而且FF比FE大1。
如果一個文本文件的頭兩個字節是FE FF,就表示該文件采用大頭方式;如果頭兩個字節是FF FE,就表示該文件采用小頭方式。
這兩個古怪的名稱來自英國作家斯威夫特的《格列佛游記》。在該書中,小人國里爆發了內戰,戰爭起因是人們爭論,
吃雞蛋時究竟是從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開。為了這件事情,前后爆發了六次戰爭,
一個皇帝送了命,另一個皇帝丟了王位。 因此,第一個字節在前,就是”大頭方式“(Big endian),第二個字節在前就是”小頭方式“(Little endian)。
實例:
打開”記事本“程序Notepad.exe,新建一個文本文件,內容就是一個”嚴“字,依次采用ANSI,Unicode,Unicode big endian 和 UTF-8編碼方式保存。 然后,用文本編輯軟件UltraEdit中的”十六進制功能“,觀察該文件的內部編碼方式。 1)ANSI:文件的編碼就是兩個字節“D1 CF”,這正是“嚴”的GB2312編碼,這也暗示GB2312是采用大頭方式存儲的。 2)Unicode:編碼是四個字節“FF FE 25 4E”,其中“FF FE”表明是小頭方式存儲,真正的編碼是4E25。 3)Unicode big endian:編碼是四個字節“FE FF 4E 25”,其中“FE FF”表明是大頭方式存儲。 4)UTF-8:編碼是六個字節“EF BB BF E4 B8 A5”,
前三個字節“EF BB BF”表示這是UTF-8編碼,
后三個“E4B8A5”就是“嚴”的具體編碼,它的存儲順序與編碼順序是一致的。
Unicode和utf16是一一對應的。他倆都是2個字節。只是傳輸英文會浪費一倍空間。
打開文件 FE FF 表示UTF-16.
"漢"對應的unicode是6C49
UTF-16表示的話就是01101100 01001001(共16 bit,兩個字節).UTF-16不需要用啥字符來做標志,所以兩字節也就是2的16次能表示65536個字符.
而UTF-8由於里面有額外的標志信息,所有一個字節只能表示2的7次方128個字符,兩個字節只能表示2的11次方2048個字符.而三個字節能表示2的16次方,65536個字符.
由於"漢"的編碼27721大於2048了所有兩個字節還不夠,只能用三個字節來表示.
所有要用1110xxxx 10xxxxxx 10xxxxxx這種格式.把27721對應的二進制從左到右(little endia 從右到左)填充XXX符號.
Unicode版本2
前面說的都是unicode的第一個版本.但65536顯然不算太多的數字,用它來表示常用的字符是沒一點問題.足夠了,但如果加上很多特殊的就也不夠了.於是從1996年開始又來了第二個版本.用四個字節表示所有字符.這樣就出現了UTF-8,UTF16,UTF-32.原理和之前肯定是完全一樣的,UTF-32就是把所有的字符都用32bit也就是4個字節來表示.然后UTF-8,UTF-16就視情況而定了.UTF-8可以選擇1至8個字節中的任一個來表示.而UTF-16只能是選兩字節或四字節..由於unicode版本2的原理完全是一樣的,就不多說了.
前面說了要知道具體是哪種編碼方式,需要判斷文本開頭的標志,下面是所有編碼對應的開頭標志
EF BB BF UTF-8
FE FF UTF-16/UCS-2, little endian
FF FE UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian.
00 00 FE FF UTF-32/UCS-4, big-endian.
其中的UCS就是前面說的ISO制定的標准,和Unicode是完全一樣的,只不過名字不一樣.ucs-2對應utf-16,ucs-4對應UTF-32.UTF-8是沒有對應的UCS
UTF-16 並不是一個完美的選擇,它存在幾個方面的問題:
- UTF-16 能表示的字符數有 6 萬多,看起來很多,但是實際上目前 Unicode 5.0 收錄的字符已經達到 99024 個字符,早已超過 UTF-16 的存儲范圍;這直接導致 UTF-16 地位頗為尷尬——如果誰還在想着只要使用 UTF-16 就可以高枕無憂的話,恐怕要失望了
- UTF-16 存在大小端字節序問題,這個問題在進行信息交換時特別突出——如果字節序未協商好,將導致亂碼;如果協商好,但是雙方一個采用大端一個采用小端,則必然有一方要進行大小端轉換,性能損失不可避免(大小端問題其實不像看起來那么簡單,有時會涉及硬件、操作系統、上層軟件多個層次,可能會進行多次轉換)
- 另外,容錯性低有時候也是一大問題——局部的字節錯誤,特別是丟失或增加可能導致所有后續字符全部錯亂,錯亂后要想恢復,可能很簡單,也可能會非常困難。(這一點在日常生活里大家感覺似乎無關緊要,但是在很多特殊環境下卻是巨大的缺陷)
目前支撐我們繼續使用 UTF-16 的理由主要是考慮到它是雙字節的,在計算字符串長度、執行索引操作時速度很快。當然這些優點 UTF-32 都具有,但很多人畢竟還是覺得 UTF-32 太占空間了。
反過來 UTF-8 也不完美,也存在一些問題:
- 文化上的不平衡——對於歐美地區一些以英語為母語的國家 UTF-8 簡直是太棒了,因為它和 ASCII 一樣,一個字符只占一個字節,沒有任何額外的存儲負擔;但是對於中日韓等國家來說,UTF-8 實在是太冗余,一個字符竟然要占用 3 個字節,存儲和傳輸的效率不但沒有提升,反而下降了。所以歐美人民常常毫不猶豫的采用 UTF-8,而我們卻老是要猶豫一會兒
- 變長字節表示帶來的效率問題——大家對 UTF-8 疑慮重重的一個問題就是在於其因為是變長字節表示,因此無論是計算字符數,還是執行索引操作效率都不高。為了解決這個問題,常常會考慮把 UTF-8 先轉換為 UTF-16 或者 UTF-32 后再操作,操作完畢后再轉換回去。而這顯然是一種性能負擔。
當然,UTF-8 的優點也不能忘了:
- 字符空間足夠大,未來 Unicode 新標准收錄更多字符,UTF-8 也能妥妥的兼容,因此不會再出現 UTF-16 那樣的尷尬
- 不存在大小端字節序問題,信息交換時非常便捷
- 容錯性高,局部的字節錯誤(丟失、增加、改變)不會導致連鎖性的錯誤,因為 UTF-8 的字符邊界很容易檢測出來,這是一個巨大的優點(正是為了實現這一點,咱們中日韓人民不得不忍受 3 字節 1 個字符的苦日子)
那么到底該如何選擇呢?
因為無論是 UTF-8 和 UTF-16/32 都各有優缺點,因此 選擇的時候應當立足於實際的應用場景。例如在我的習慣中,存儲在磁盤上或進行網絡交換時都會采用 UTF-8,而在程序內部進行處理時則轉換為 UTF-16/32。對於大多數簡單的程序來說,這樣做既可以保證信息交換時容易實現相互兼容,同時在內部處理時會比較簡單,性能也還算不錯。(基本上只要你的程序不是 I/O 密集型的都可以這么干,當然這只是我粗淺的認識范圍內的經驗,很可能會被無情的反駁)
稍微再展開那么一點點……
在一些特殊的領域,字符編碼的選擇會成為一個很關鍵的問題。特別是一些高性能網絡處理程序里更是如此。這時采用一些特殊的設計技巧,可以緩解性能和字符集選擇之間的矛盾。例如對於內容檢測/過濾系統,需要面對任何可能的字符編碼,這時如果還采用把各種不同的編碼都轉換為同一種編碼后再處理的方案,那么性能下降將會很顯著。而如果采用多字符編碼支持的有限狀態機方案,則既能夠無需轉換編碼,同時又能夠以極高的性能進行處理。當然如何從規則列表生成有限狀態機,如何使得有限狀態機支持多編碼,以及這將帶來哪些限制,已經又成了另外的問題了。
轉換編碼:
/** 中文字符串轉UTF-8與GBK碼示例 */ public static void tttt() throws Exception { String old = "手機銀行"; //中文轉換成UTF-8編碼(16進制字符串) StringBuffer utf8Str = new StringBuffer(); byte[] utf8Decode = old.getBytes("utf-8"); for (byte b : utf8Decode) { utf8Str.append(Integer.toHexString(b & 0xFF)); } // utf8Str.toString()=====e6898be69cbae993b6e8a18c // System.out.println("UTF-8字符串e6898be69cbae993b6e8a18c轉換成中文值======" + new String(utf8Decode, "utf-8"));//-------手機銀行 //中文轉換成GBK碼(16進制字符串) StringBuffer gbkStr = new StringBuffer(); byte[] gbkDecode = old.getBytes("gbk"); for (byte b : gbkDecode) { gbkStr.append(Integer.toHexString(b & 0xFF)); } // gbkStr.toString()=====cad6bbfad2f8d0d0 // System.out.println("GBK字符串cad6bbfad2f8d0d0轉換成中文值======" + new String(gbkDecode, "gbk"));//----------手機銀行 //16進制字符串轉換成中文 byte[] bb = HexString2Bytes(gbkStr.toString()); bb = HexString2Bytes("CAD6BBFAD2F8D0D0000000000000000000000000"); byte[] cc = hexToByte("CAD6BBFAD2F8D0D0000000000000000000000000", 20); String aa = new String(bb, "gbk"); System.out.println("aa====" + aa); } /** * 把16進制字符串轉換成字節數組 * @param hexstr * @return */ public static byte[] HexString2Bytes(String hexstr) { byte[] b = new byte[hexstr.length() / 2]; int j = 0; for (int i = 0; i < b.length; i++) { char c0 = hexstr.charAt(j++); char c1 = hexstr.charAt(j++); b[i] = (byte) ((parse(c0) << 4) | parse(c1)); } return b; } private static int parse(char c) { if (c >= 'a') return (c - 'a' + 10) & 0x0f; if (c >= 'A') return (c - 'A' + 10) & 0x0f; return (c - '0') & 0x0f; }
處理content-disposition http header里面。
chrome內部使用utf8來處理。
if (!base::IsStringASCII(encoded_word)) { // Try UTF-8, referrer_charset and the native OS default charset in turn. if (base::IsStringUTF8(encoded_word)) { *output = encoded_word;//utf8直接返回 } else { base::string16 utf16_output;
//對應未知編碼,先轉成utf16,在轉成utf8輸出。字符集charset沒有設置,就用系統當前默認的。windows為GBK。windows10可以自己把默認字符集改為UTF8。 if (!referrer_charset.empty() && ConvertToUTF16(encoded_word, referrer_charset.c_str(), &utf16_output)) { *output = base::UTF16ToUTF8(utf16_output); } else { *output = base::WideToUTF8(base::SysNativeMBToWide(encoded_word)); } }
*parse_result_flags |= HttpContentDisposition::HAS_NON_ASCII_STRINGS;
electron層:
d:\dev\electron7\src\electron\shell\browser\api\atom_api_download_item.cc
std::string DownloadItem::GetFilename() const { return base::UTF16ToUTF8( net::GenerateFileName(GetURL(), GetContentDisposition(), std::string(), download_item_->GetSuggestedFilename(), GetMimeType(), "download") .LossyDisplayName()); } std::string DownloadItem::GetContentDisposition() const { return download_item_->GetContentDisposition(); }
還是把上面的分析disposition跑了一遍,最終到net模塊的:
base::string16 GetSuggestedFilenameImpl( const GURL& url, const std::string& content_disposition, const std::string& referrer_charset, const std::string& suggested_name, const std::string& mime_type, const std::string& default_name, bool should_replace_extension, ReplaceIllegalCharactersFunction replace_illegal_characters_function) { // TODO: this function to be updated to match the httpbis recommendations. // Talk to abarth for the latest news. // We don't translate this fallback string, "download". If localization is // needed, the caller should provide localized fallback in |default_name|. static const base::FilePath::CharType kFinalFallbackName[] = FILE_PATH_LITERAL("download"); std::string filename; // In UTF-8 bool overwrite_extension = false; bool is_name_from_content_disposition = false; // Try to extract a filename from content-disposition first. if (!content_disposition.empty()) { HttpContentDisposition header(content_disposition, referrer_charset); filename = header.filename(); if (!filename.empty()) is_name_from_content_disposition = true; } // Then try to use the suggested name. if (filename.empty() && !suggested_name.empty()) filename = suggested_name; // Now try extracting the filename from the URL. GetFileNameFromURL() only // looks at the last component of the URL and doesn't return the hostname as a // failover. if (filename.empty()) filename = GetFileNameFromURL(url, referrer_charset, &overwrite_extension); // Finally try the URL hostname, but only if there's no default specified in // |default_name|. Some schemes (e.g.: file:, about:, data:) do not have a // host name. if (filename.empty() && default_name.empty() && url.is_valid() && !url.host().empty()) { // TODO(jungshik) : Decode a 'punycoded' IDN hostname. (bug 1264451) filename = url.host(); } bool replace_trailing = false; base::FilePath::StringType result_str, default_name_str; #if defined(OS_WIN) replace_trailing = true; result_str = base::UTF8ToUTF16(filename); default_name_str = base::UTF8ToUTF16(default_name); #elif defined(OS_POSIX) || defined(OS_FUCHSIA) result_str = filename; default_name_str = default_name; #else #error Unsupported platform #endif SanitizeGeneratedFileName(&result_str, replace_trailing); if (result_str.find_last_not_of(FILE_PATH_LITERAL("-_")) == base::FilePath::StringType::npos) { result_str = !default_name_str.empty() ? default_name_str : base::FilePath::StringType(kFinalFallbackName); overwrite_extension = false; } replace_illegal_characters_function(&result_str, '_'); base::FilePath result(result_str); overwrite_extension |= should_replace_extension; // extension should not appended to filename derived from // content-disposition, if it does not have one. // Hence mimetype and overwrite_extension values are not used. if (is_name_from_content_disposition) GenerateSafeFileName("", false, &result); else GenerateSafeFileName(mime_type, overwrite_extension, &result); base::string16 result16; if (!FilePathToString16(result, &result16)) { result = base::FilePath(default_name_str); if (!FilePathToString16(result, &result16)) { result = base::FilePath(kFinalFallbackName); FilePathToString16(result, &result16); } } return result16; }
base::FilePath GenerateFileNameImpl( const GURL& url, const std::string& content_disposition, const std::string& referrer_charset, const std::string& suggested_name, const std::string& mime_type, const std::string& default_file_name, bool should_replace_extension, ReplaceIllegalCharactersFunction replace_illegal_characters_function) { base::string16 file_name = GetSuggestedFilenameImpl( url, content_disposition, referrer_charset, suggested_name, mime_type, default_file_name, should_replace_extension, replace_illegal_characters_function); #if defined(OS_WIN) base::FilePath generated_name(file_name); #elif defined(OS_POSIX) || defined(OS_FUCHSIA) base::FilePath generated_name( base::SysWideToNativeMB(base::UTF16ToWide(file_name))); #endif DCHECK(!generated_name.empty()); return generated_name; }
http網頁跳轉時調用:
1,
2
3
4,這里把原始編碼做轉換,會調用 HttpContentDisposition::HttpContentDisposition的parse函數,調用到
bool DecodeWord(const std::string& encoded_word
5,開始遠程調用:
6posttask到
7,轉到delegate_->StartDownloadItem
8,新的download走:
GetNextId(base::BindOnce(&DownloadManagerImpl::CreateNewDownloadItemToStart,
weak_factory_.GetWeakPtr(), std::move(info),
on_started, std::move(callback)));
9,再到
download::DownloadItemImpl* download = CreateActiveItem(id, *info);
10,這里還是沒轉碼的值:
從跟蹤代碼來看,content-disposition存放的是http response的raw header。
在HttpContentDisposition類的filename_成員才會存放轉換了的編碼。
js里面全部是utf8編碼,導致gbk的響應頭無法輸出????
[17240:0923/170223.551:FATAL:values.cc(210)] Check failed: IsStringUTF8(string_value_). Backtrace: base::debug::CollectStackTrace [0x00007FFC111911E0+48] (D:\dev\electron7\src\base\debug\stack_trace_win.cc:284) base::debug::StackTrace::StackTrace [0x00007FFC11191180+80] (D:\dev\electron7\src\base\debug\stack_trace.cc:206) base::debug::StackTrace::StackTrace [0x00007FFC11183B58+40] (D:\dev\electron7\src\base\debug\stack_trace.cc:203) logging::LogMessage::~LogMessage [0x00007FFC11203D2F+143] (D:\dev\electron7\src\base\logging.cc:629) base::Value::Value [0x00007FFC114714E2+290] (D:\dev\electron7\src\base\values.cc:211) base::Value::Value [0x00007FFC114719AE+62] (D:\dev\electron7\src\base\values.cc:206) std::_Default_allocator_traits<std::allocator<base::Value> >::construct<base::Value,std::basic_string<char,std::char_traits<char>,std::allocator<char> > &> [0x00007FF61263DD08+88] (C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include\xmemory:671) std::vector<base::Value,std::allocator<base::Value> >::_Emplace_reallocate<std::basic_string<char,std::char_traits<char>,std::allocator<char> > &> [0x00007FF61263DBB2+386] (C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include\vector:750) std::vector<base::Value,std::allocator<base::Value> >::emplace_back<std::basic_string<char,std::char_traits<char>,std::allocator<char> > &> [0x00007FF61263D930+128] (C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include\vector:708) electron::api::`anonymous namespace'::HttpResponseHeadersToV8 [0x00007FF61263D3B8+392] (D:\dev\electron7\src\electron\shell\browser\api\atom_api_web_request_ns.cc:115) electron::api::`anonymous namespace'::ToDictionary [0x00007FF61263C880+1280] (D:\dev\electron7\src\electron\shell\browser\api\atom_api_web_request_ns.cc:138) electron::api::`anonymous namespace'::FillDetails<extensions::WebRequestInfo *,network::ResourceRequest> [0x00007FF61263A7E7+55] (D:\dev\electron7\src\electron\shell\browser\api\atom_api_web_request_ns.cc:180) electron::api::WebRequestNS::HandleSimpleEvent<network::ResourceRequest> [0x00007FF61262B581+305] (D:\dev\electron7\src\electron\shell\browser\api\atom_api_web_request_ns.cc:414) electron::api::WebRequestNS::OnResponseStarted [0x00007FF61262B430+112] (D:\dev\electron7\src\electron\shell\browser\api\atom_api_web_request_ns.cc:316) electron::ProxyingURLLoaderFactory::InProgressRequest::ContinueToResponseStarted [0x00007FF612709917+1079] (D:\dev\electron7\src\electron\shell\browser\net\proxying_url_loader_factory.cc:512) electron::ProxyingURLLoaderFactory::InProgressRequest::OnReceiveResponse [0x00007FF612709349+265] (D:\dev\electron7\src\electron\shell\browser\net\proxying_url_loader_factory.cc:208) network::mojom::URLLoaderClientStubDispatch::Accept [0x00007FF612BE9A24+980] (D:\dev\electron7\src\out\Testing\gen\services\network\public\mojom\url_loader.mojom.cc:1191) network::mojom::URLLoaderClientStub<mojo::RawPtrImplRefTraits<network::mojom::URLLoaderClient> >::Accept [0x00007FF6127111F2+98] (D:\dev\electron7\src\out\Testing\gen\services\network\public\mojom\url_loader.mojom.h:296) mojo::InterfaceEndpointClient::HandleValidatedMessage [0x00007FFC343B8EEF+1935] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\interface_endpoint_client.cc:554) mojo::InterfaceEndpointClient::HandleIncomingMessageThunk::Accept [0x00007FFC343B8751+33] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\interface_endpoint_client.cc:140) mojo::FilterChain::Accept [0x00007FFC343B73C9+393] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\filter_chain.cc:40) mojo::InterfaceEndpointClient::HandleIncomingMessage [0x00007FFC343BBCD5+213] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\interface_endpoint_client.cc:357) mojo::internal::MultiplexRouter::ProcessIncomingMessage [0x00007FFC343CE6F2+1666] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\multiplex_router.cc:876) mojo::internal::MultiplexRouter::Accept [0x00007FFC343CDC41+673] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\multiplex_router.cc:598) mojo::FilterChain::Accept [0x00007FFC343B73C9+393] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\filter_chain.cc:40) mojo::Connector::DispatchMessageW [0x00007FFC3439F6F4+1396] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\connector.cc:513) mojo::Connector::ReadAllAvailableMessages [0x00007FFC343A0D53+675] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\connector.cc:589) mojo::Connector::OnHandleReadyInternal [0x00007FFC343A088A+378] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\connector.cc:424) mojo::Connector::OnWatcherHandleReady [0x00007FFC343A06FB+27] (D:\dev\electron7\src\mojo\public\cpp\bindings\lib\connector.cc:384) base::internal::FunctorTraits<void (mojo::Connector::*)(unsigned int),void>::Invoke<void (mojo::Connector::*)(unsigned int),mojo::Connector *,unsigned int> [0x00007FFC343A90B5+69] (D:\dev\electron7\src\base\bind_internal.h:499) base::internal::InvokeHelper<0,void>::MakeItSo<void (mojo::Connector::*const &)(unsigned int),mojo::Connector *,unsigned int> [0x00007FFC343A8FFD+77] (D:\dev\electron7\src\base\bind_internal.h:599) base::internal::Invoker<base::internal::BindState<void (mojo::Connector::*)(unsigned int),base::internal::UnretainedWrapper<mojo::Connector> >,void (unsigned int)>::RunImpl<void (mojo::Connector::*const &)(unsigned int),const std::tuple<base::internal::Un [0x00007FFC343A8F91+113] (D:\dev\electron7\src\base\bind_internal.h:672) base::internal::Invoker<base::internal::BindState<void (mojo::Connector::*)(unsigned int),base::internal::UnretainedWrapper<mojo::Connector> >,void (unsigned int)>::Run [0x00007FFC343A8E85+101] (D:\dev\electron7\src\base\bind_internal.h:654) base::RepeatingCallback<void (unsigned int)>::Run [0x00007FFC34397DA8+104] (D:\dev\electron7\src\base\callback.h:132) mojo::SimpleWatcher::DiscardReadyState [0x00007FFC343A36B0+32] (D:\dev\electron7\src\mojo\public\cpp\system\simple_watcher.h:195) base::internal::FunctorTraits<void (*)(const base::RepeatingCallback<void (unsigned int)> &, unsigned int, const mojo::HandleSignalsState &),void>::Invoke<void (*const &)(const base::RepeatingCallback<void (unsigned int)> &, unsigned int, const mojo::Hand [0x00007FFC343A3AD6+102] (D:\dev\electron7\src\base\bind_internal.h:399) base::internal::InvokeHelper<0,void>::MakeItSo<void (*const &)(const base::RepeatingCallback<void (unsigned int)> &, unsigned int, const mojo::HandleSignalsState &),const base::RepeatingCallback<void (unsigned int)> &,unsigned int,const mojo::HandleSignal [0x00007FFC343A3A36+102] (D:\dev\electron7\src\base\bind_internal.h:599) base::internal::Invoker<base::internal::BindState<void (*)(const base::RepeatingCallback<void (unsigned int)> &, unsigned int, const mojo::HandleSignalsState &),base::RepeatingCallback<void (unsigned int)> >,void (unsigned int, const mojo::HandleSignalsSt [0x00007FFC343A39B6+134] (D:\dev\electron7\src\base\bind_internal.h:672) base::internal::Invoker<base::internal::BindState<void (*)(const base::RepeatingCallback<void (unsigned int)> &, unsigned int, const mojo::HandleSignalsState &),base::RepeatingCallback<void (unsigned int)> >,void (unsigned int, const mojo::HandleSignalsSt [0x00007FFC343A3853+131] (D:\dev\electron7\src\base\bind_internal.h:654) base::RepeatingCallback<void (unsigned int, const mojo::HandleSignalsState &)>::Run [0x00007FFC34A9C281+129] (D:\dev\electron7\src\base\callback.h:132) mojo::SimpleWatcher::OnHandleReady [0x00007FFC34A9BE11+753] (D:\dev\electron7\src\mojo\public\cpp\system\simple_watcher.cc:293) base::internal::FunctorTraits<void (mojo::SimpleWatcher::*)(int, unsigned int, const mojo::HandleSignalsState &),void>::Invoke<void (mojo::SimpleWatcher::*)(int, unsigned int, const mojo::HandleSignalsState &),base::WeakPtr<mojo::SimpleWatcher>,int,unsign [0x00007FFC34A9CE37+135] (D:\dev\electron7\src\base\bind_internal.h:499) base::internal::InvokeHelper<1,void>::MakeItSo<void (mojo::SimpleWatcher::*)(int, unsigned int, const mojo::HandleSignalsState &),base::WeakPtr<mojo::SimpleWatcher>,int,unsigned int,mojo::HandleSignalsState> [0x00007FFC34A9CC96+166] (D:\dev\electron7\src\base\bind_internal.h:622) base::internal::Invoker<base::internal::BindState<void (mojo::SimpleWatcher::*)(int, unsigned int, const mojo::HandleSignalsState &),base::WeakPtr<mojo::SimpleWatcher>,int,unsigned int,mojo::HandleSignalsState>,void ()>::RunImpl<void (mojo::SimpleWatcher: [0x00007FFC34A9CBCA+186] (D:\dev\electron7\src\base\bind_internal.h:672) base::internal::Invoker<base::internal::BindState<void (mojo::SimpleWatcher::*)(int, unsigned int, const mojo::HandleSignalsState &),base::WeakPtr<mojo::SimpleWatcher>,int,unsigned int,mojo::HandleSignalsState>,void ()>::RunOnce [0x00007FFC34A9C9CE+78] (D:\dev\electron7\src\base\bind_internal.h:641) base::OnceCallback<void ()>::Run [0x00007FFC111726B1+97] (D:\dev\electron7\src\base\callback.h:99) base::TaskAnnotator::RunTask [0x00007FFC11341335+1605] (D:\dev\electron7\src\base\task\common\task_annotator.cc:144) base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl [0x00007FFC113794AC+1804] (D:\dev\electron7\src\base\task\sequence_manager\thread_controller_with_message_pump_impl.cc:366) base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoSomeWork [0x00007FFC11378B2F+191] (D:\dev\electron7\src\base\task\sequence_manager\thread_controller_with_message_pump_impl.cc:221) base::MessagePumpForUI::DoRunLoop [0x00007FFC11220449+457] (D:\dev\electron7\src\base\message_loop\message_pump_win.cc:217) base::MessagePumpWin::Run [0x00007FFC1121EBA4+292] (D:\dev\electron7\src\base\message_loop\message_pump_win.cc:76) base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run [0x00007FFC1137A87E+910] (D:\dev\electron7\src\base\task\sequence_manager\thread_controller_with_message_pump_impl.cc:467) base::RunLoop::Run [0x00007FFC112DE335+901] (D:\dev\electron7\src\base\run_loop.cc:156) content::BrowserMainLoop::MainMessageLoopRun [0x00007FFBFEC4AADD+157] (D:\dev\electron7\src\content\browser\browser_main_loop.cc:1511) content::BrowserMainLoop::RunMainMessageLoopParts [0x00007FFBFEC4A8D0+528] (D:\dev\electron7\src\content\browser\browser_main_loop.cc:1031) content::BrowserMainRunnerImpl::Run [0x00007FFBFEC500DF+335] (D:\dev\electron7\src\content\browser\browser_main_runner_impl.cc:149) content::BrowserMain [0x00007FFBFEC4357C+284] (D:\dev\electron7\src\content\browser\browser_main.cc:47) content::RunBrowserProcessMain [0x00007FFC0145D468+168] (D:\dev\electron7\src\content\app\content_main_runner_impl.cc:556) content::ContentMainRunnerImpl::RunServiceManager [0x00007FFC0145EC76+1334] (D:\dev\electron7\src\content\app\content_main_runner_impl.cc:963) content::ContentMainRunnerImpl::Run [0x00007FFC0145E656+614] (D:\dev\electron7\src\content\app\content_main_runner_impl.cc:871) content::ContentServiceManagerMainDelegate::RunEmbedderProcess [0x00007FFC01459BA7+55] (D:\dev\electron7\src\content\app\content_service_manager_main_delegate.cc:52) service_manager::Main [0x00007FFBDA202323+1731] (D:\dev\electron7\src\services\service_manager\embedder\main.cc:423) content::ContentMain [0x00007FFC0145D32F+95] (D:\dev\electron7\src\content\app\content_main.cc:20) wWinMain [0x00007FF6123C20BC+1244] (D:\dev\electron7\src\electron\shell\app\atom_main.cc:168) invoke_main [0x00007FF6150A6B42+50] (d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:123) __scrt_common_main_seh [0x00007FF6150A6C7E+302] (d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288) __scrt_common_main [0x00007FF6150A6CFE+14] (d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:331) wWinMainCRTStartup [0x00007FF6150A6D19+9] (d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_wwinmain.cpp:17) BaseThreadInitThunk [0x00007FFC4B4C7BD4+20] RtlUserThreadStart [0x00007FFC4C86CE51+33] Task trace: Backtrace: mojo::SimpleWatcher::Context::Notify [0x00007FFC34A9C590+528] (D:\dev\electron7\src\mojo\public\cpp\system\simple_watcher.cc:120) IPC message handler context: 0x51C3F41F
在electron中,當http服務器返回http header是gbk的content-disposition下載時,文件名保存亂碼。在electron調用時注冊了onHeadersReceived回調。(寫在app.whenReady().then(function xx)里面)
//win.webContents.session. session.defaultSession.webRequest.onHeadersReceived((details, callback) => { // logger.debug(`onHeadersReceived: url:${details.url}, details:${JSON.stringify(details)}`); console.log("========= electron onHeadersReceived ====================") /* for (const key in details.responseHeaders) { const lowerKey = key.toLowerCase(); switch (lowerKey) { case 'x-frame-options': case 'content-security-policy': // logger.debug(`Received header ${key}: ${details.responseHeaders[key]}`); delete details.responseHeaders[key]; break; } } */ // console.log(`Response headers: ${JSON.stringify(details.responseHeaders)}`); callback({ cancel: false, responseHeaders: details.responseHeaders, statusLine: details.statusLine, }); });
調查發現是這里會檢查我們要做的編碼為utf8:
它是這里上層調用的:
最后我們看到在做gin::ConvertToV8轉碼時,對String會用UTF8編碼:
// Returns the filename determined from the last component of the path portion // of the URL. Returns an empty string if the URL doesn't have a path or is // invalid. If the generated filename is not reliable, // |should_overwrite_extension| will be set to true, in which case a better // extension should be determined based on the content type. std::string GetFileNameFromURL(const GURL& url, const std::string& referrer_charset, bool* should_overwrite_extension) { // about: and data: URLs don't have file names, but esp. data: URLs may // contain parts that look like ones (i.e., contain a slash). Therefore we // don't attempt to divine a file name out of them. if (!url.is_valid() || url.SchemeIs("about") || url.SchemeIs("data")) return std::string(); std::string unescaped_url_filename = UnescapeBinaryURLComponent(url.ExtractFileName(), UnescapeRule::NORMAL); // The URL's path should be escaped UTF-8, but may not be. std::string decoded_filename = unescaped_url_filename; if (!base::IsStringUTF8(decoded_filename)) { // TODO(jshin): this is probably not robust enough. To be sure, we need // encoding detection. base::string16 utf16_output; if (!referrer_charset.empty() && ConvertToUTF16(unescaped_url_filename, referrer_charset.c_str(), &utf16_output)) { decoded_filename = base::UTF16ToUTF8(utf16_output); } else { decoded_filename = base::WideToUTF8(base::SysNativeMBToWide(unescaped_url_filename)); } } // If the URL contains a (possibly empty) query, assume it is a generator, and // allow the determined extension to be overwritten. *should_overwrite_extension = !decoded_filename.empty() && url.has_query(); return decoded_filename; }
去掉escape字串: D:\dev\electron7\src\net\base\escape.h