UNPKG

cejs

Version:

A JavaScript module framework that is simple to use.

968 lines (863 loc) 30.1 kB
/** * @name CeL file function * @fileoverview 本檔案包含了 file functions。 * @since * @see <a href="http://dev.w3.org/2006/webapi/FileAPI/" accessdate="2010/6/20 * 14:49">File API</a> */ 'use strict'; // 'use asm'; // -------------------------------------------------------------------------------------------- // 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。 typeof CeL === 'function' && CeL.run({ // module name name : 'application.storage.file', // Math.log2() require : 'data.code.compatibility.' // .chunk() + '|data.native.' // MIME_of() // +'|application.net.MIME.' , // 設定不匯出的子函式。 // no_extend : '*', // 為了方便格式化程式碼,因此將 module 函式主體另外抽出。 code : module_code }); function module_code(library_namespace) { /** * null module constructor * * @class 檔案操作相關之 function。 */ var _// JSDT:_module_ = function() { // null module constructor }; /** * for JSDT: 有 prototype 才會將之當作 Class */ _// JSDT:_module_ .prototype = {}; // --------------------------------------------------------------- // path處理 // @see CeL.simplify_path() // 去除hostname等,如輸入http://hostname/aaa/bbb/ccc得到aaa/bbb/ccc/ // 假如輸入的格式不正確,可能得出不預期的回應值! /* * 對library_namespace.env.path_separator.length>1的情形(嚴謹) */ function deprecated_getPathOnly(p) { // discard hash & search var i = p.lastIndexOf('?'), j = p.lastIndexOf('#'), dirSpL = library_namespace.env.path_separator.length; if (i === -1) i = j; else if (j !== -1 && i > j) i = j; if (i !== -1) p = p.slice(0, i); // 去除http://hostname/等 if (p.slice(0, 5) === 'file:///') // 對file:///特別處理! p = p.substr('file:///'.length); else if ((i = p.indexOf(':' + library_namespace.env.path_separator // + library_namespace.env.path_separator)) !== -1 // && (i = p.indexOf(library_namespace.env.path_separator, i + (':' // + library_namespace.env.path_separator // + library_namespace.env.path_separator).length)) !== -1) // http://hostname/path→path p = p.substr(i + dirSpL); else if (p.slice(0, dirSpL) === library_namespace.env.path_separator) // /usr/local/→usr/local/ if (p.substr(dirSpL, dirSpL) !== library_namespace.env.path_separator) p = p.substr(dirSpL); else if ((i = p.indexOf(library_namespace.env.path_separator, dirSpL + dirSpL)) > dirSpL + dirSpL) // 去除\\hostname\ p = p.substr(i + dirSpL); else if (i !== -1) // \\\zzzz的情形:不合法的路徑 throw new Error(1, 'illegal path:' + p); return p; } // 對library_namespace.env.path_separator.length==1的情形簡化 // getPathOnly[generateCode.dLK]='library_namespace.env.path_separator';//,isFile function getPathOnly(p) { // discard hash & search var i = p.lastIndexOf('?'), j = p.lastIndexOf('#'); if (i === -1) i = j; else if (j !== -1 && i > j) i = j; if (i !== -1) p = p.slice(0, i); // 去除http://hostname/等 if (p.slice(0, 8) === 'file:///') // 對file:///(應該是file:)特別處理! p = p.substr(8); else if ((i = p.indexOf(':' + library_namespace.env.path_separator + library_namespace.env.path_separator)) !== -1 && (i = p.indexOf(library_namespace.env.path_separator, i + 3) !== -1)) // http://hostname/path→path p = p.substr(i + 1); else if (p.charAt(0) === library_namespace.env.path_separator) // /usr/local/→usr/local/ if (p.charAt(1) !== library_namespace.env.path_separator) p = p.substr(1); else if ((i = p.indexOf(library_namespace.env.path_separator, 2)) > 2) // 去除\\hostname\ 不去除:.replace(/[^\\]+$/,'') p = p.substr(i + 1); else if (i !== -1) // \\\zzzz的情形:不合法的路徑 throw new Error(1, 'illegal path:' + p); // !isWeb()&&~ if (typeof isFile === 'function' && isFile(p)) p = p.replace(new RegExp( library_namespace.env.path_separator_pattern + '[^' + library_namespace.env.path_separator_pattern + ']+$'), library_namespace.env.path_separator); return p; } var path_separator_candidates = [ '/', '\\' ], path_separator_candidates_length = path_separator_candidates.length, // path_separator_candidates_Regex // =new RegExp(path_separator_candidates.join(''), 'g'), path_separator_RegExp; _// JSDT:_module_ .guess_path_separator = function(path) { if (typeof path !== 'string' || !path) return; var count = 0, candidate, i, m; if (!path_separator_RegExp) // 初始設定 for (path_separator_RegExp = [], i = 0; i < path_separator_candidates_length; i++) path_separator_RegExp[i] = new RegExp('\\' + path_separator_candidates[i], 'g'); // 偵測最符合的 for (i = 0; i < path_separator_candidates_length; i++) { m = path.match(path_separator_RegExp[i]); if (m && m.length > count) count = m.length, candidate = path_separator_candidates[i]; } return candidate || library_namespace.env.path_separator; }; _// JSDT:_module_ . /** * 取得 base_path 相對於 working_path 的路徑。 * <p> * 當前處於 working_path,欲取得 base_path 下所指到的 resource_file_path,可使用: <code> * get_relative_path(base_path, working_path) + resource_file_path * </code> * </p> * TODO: 找出最大連續相同路徑。 * * @example <code> CeL.run('application.storage.file',function(){CeL.set_debug(2);CeL.log(CeL.get_relative_path('a/b/same/d/file','same/e/'));}); CeL.set_debug(2);CeL.get_relative_path('a/b/same/d/f/file','e/r/t/same/e/',1); base_path = CeL.get_relative_path('kanashimi/www/cgi-bin/game/'); // 想要保持 Protocol,但卻是不同機器時。 <a href="http://nedbatchelder.com/blog/200710.html#e20071017T215538" accessdate="2011/8/28 0:18" title="Ned Batchelder: October 2007">Http-https transitions and relative URLs</a> CeL.get_relative_path('cgi-bin/game/photo/','//lyrics.meicho.com.tw/game/game.pl?seg=diary21',1); * </code> * * @param {String} * base_path 給定 base path 的範本結構, base_path_structure. * @param {String} * working_path 當前處於 working_path. * @param {Boolean} * get_full_path 藉由 working_path 推測 base path 的 full path. * 否則回傳相對路徑(relative path),會增加 ../ 等。 * @returns {String} 推測的 base path 相對於 working_path 的 path. * * @since 2003/10/1 15:57 * @since 2011/8/28 00:16:40 * @requres getPathOnly,library_namespace.env.path_separator,library_namespace.env.path_separator_pattern * @_memberOf _module_ */ get_relative_path = function(base_path, working_path, get_full_path) { library_namespace.debug('phase 1: 簡化並審查 path<br />base_path [' + base_path + ']<br />working_path [' + working_path + ']', 2, 'get_relative_path'); if (!working_path) working_path = library_namespace.get_script_base_path(); else working_path = library_namespace.simplify_path(working_path); if (!working_path) return; if (!base_path) base_path = (library_namespace.is_WWW() ? location.href : typeof WshShell === 'object' ? WshShell.CurrentDirectory : typeof WScript === 'object' ? WScript.ScriptFullName // TODO: path_separator_candidates : '').replace(/[^\/\\]+$/, ''); else base_path = library_namespace.simplify_path(base_path); if (!base_path // base_path 需要是絕對路徑 // || !is_absolute_path(base_path) ) return working_path; library_namespace .debug( 'phase 2: 將 path 分解成 Array,從尾至頭比對,以 base_path 為基準,找尋 working_path 是否有相同的目錄名。<br />base_path [' + base_path + ']<br />working_path [' + working_path + ']', 2, 'get_relative_path'); // 在 IE 或 HTA 中,path_separator === '\\' 但輸入的 path 為 '/'; // 甚至於 working_path, base_path 兩者可能不同。 var path_separator = // library_namespace.env.path_separator _.guess_path_separator(working_path); working_path = working_path.split(path_separator); if (!working_path.at(-1)) // 防止輸入 a/b/c/ 而非 a/b/c 的情況。 working_path.pop(); base_path = base_path.split(_.guess_path_separator(base_path)); // alert(working_path + '\n' + base_path); var file_name = base_path.pop(); var index_base_path = base_path.length, index_working_path, working_path_length = working_path.length, directory, not_found = true; for (; index_base_path && not_found;) // 取得第 index_base_path 識別用目錄名 if (directory = base_path[--index_base_path]) { for (index_working_path = working_path_length; index_working_path;) if (directory === working_path[--index_working_path]) { not_found = false; // 第一個找到就 break 了。 // TODO: 找出最大連續相同路徑。 break; } } library_namespace.debug('phase 3: ' + (not_found ? +'找不到' : '找到 index: base_path[' + index_base_path + '], working_path[' + index_working_path + ']'), 2, 'get_relative_path'); if (not_found) library_namespace.debug("Can't find base directory of this file!\n" + working_path + '\n\nTreat base directory as:\n' + base_path, 2, 'get_relative_path'), directory = get_full_path ? working_path.concat(base_path) : base_path; else { if (get_full_path) directory = working_path.slice(0, index_working_path + 1); else { directory = []; for (var i = index_working_path + 1; i < working_path_length; i++) directory.push('..'); } // directory.append(base_path, index_base_path + 1); directory = directory.concat(base_path.slice(index_base_path + 1)); } directory.push(file_name); return directory.join(path_separator); }; _// JSDT:_module_ . /** * cf: get_file_name(), parse_URL * * @param {String} * path path name * @return * @_memberOf _module_ */ parse_path = function(path) { if (typeof path !== 'string' || !path) return; var path_data = { input_data : path }, m; if (m = path.match(/^(([A-Za-z]):\\)?(([^\\]+\\)*)([^\\]*)?$/)) { path_data.is_absolute = !!(path_data.drive = m[2]); path_data.path_name = m[3]; path_data.file_name = m[5]; library_namespace.debug('type: Windows/MS-DOS file path', 2); } else if (m = path .match(/^file:\/\/\/([A-Za-z]):\/(([^\/]+\/)*)([^\/]*)?$/)) { path_data.is_absolute = true; path_data.drive = m[1]; path_data.path_name = m[2]; path_data.file_name = m[4]; library_namespace.debug( 'type: URL of Windows/MS-DOS local file path', 2); } else if (m = path.match(/^((\/)?([^\/]+\/)*)([^\/]*)?$/)) { path_data.is_absolute = !!m[2]; path_data.path_name = m[1]; path_data.file_name = m[4]; library_namespace.debug('type: UNIX file path', 2); } else { library_namespace.debug('Failure to parse [' + path + ']'); } // 正規化 path_data.path_name = path_data.path_name.replace(/[\/\\]/g, library_namespace.env.path_separator); m = path_data.file_name.match(/^(.*?)(\.([^.]*))?$/); path_data.name = m[1]; path_data.extension = m[3]; path_data.path = path_data.path_name + path_data.file_name; path_data.location = path_data.drive + ':\\' + path_data.path; path_data.directory = path_data.drive + ':\\' + path_data.path_name; return path_data; }; _// JSDT:_module_ . /** * Test if local path is absolute or relative path, not very good solution. * TODO: test FULL path. * * @param {String} * local path * @return * @requires library_namespace.env.path_separator,library_namespace.env.path_separator_pattern * @_memberOf _module_ */ is_absolute_path = function(path) { // alert(typeof path + '\n' + path); return /^(\/|[A-Z]+:([\\\/]|$)|\\\\[^\\])/i.test(path); return path && (library_namespace.env.path_separator === '/' && path.charAt(0) === library_namespace.env.path_separator || new RegExp( '^(\\\\|[A-Za-z]+:)' + library_namespace.env.path_separator_pattern) .test(path)) // ?true:false ; }; // 轉成path(加'\') function turnToPath(p) { return p ? p + (p.slice(-1) == '\\' ? '' : '\\') : ''; } // 僅取得path部分(包括 library_namespace.env.path_separator),不包括檔名。 // getFilePath[generateCode.dLK]='library_namespace.env.path_separator'; function getFilePath(p) { var i = p.lastIndexOf(library_namespace.env.path_separator); if (i == -1) p += library_namespace.env.path_separator; // 相對路徑? else if (i < p.length - 1) p = p.slice(0, i + 1); // 取得path部分 return p; } // get_file_path[generateCode.dLK]='is_absolute_path,getPathOnly,get_relative_path'; /** * 傳回包括檔名之絕對/相對路徑,假如是資料夾,也會回傳資料夾路徑。可包含'.','..'等 the return value include ? # * of URI<br /> * 在Win/DOS下輸入'\'..會加上base driver。<br /> * 若只要相對路徑,可用 library_namespace.simplify_path()。取得如'..\out'的絕對路徑可用 * get_file_path('../out',1) * * @param {String}path * 路徑 * @param {Number}[mode] * 0:傳回auto(維持原狀), 1:傳回絕對路徑, 2:傳回相對路徑。 * @param {String}[base_path] * base path * * @returns */ function get_file_path(path, mode, base_path) { if (!path) return ''; // old, deprecated: if (false) return (path.lastIndexOf('\\') === -1 && path.lastIndexOf('/') === -1 ? // get_file_object getFolder(getScriptFullName()) : '') + path; var matched; if (path.charAt(0) === '\\' && (matched = get_relative_path(base_path).match( /^(\\\\|[A-Z]:)/i))) path = matched[1] + path; path = library_namespace.simplify_path(path); if (mode === 1) { // 當為相對路徑時前置 base path。 if (!is_absolute_path(path)) path = library_namespace .simplify_path((base_path ? getPathOnly(base_path) : get_relative_path()) + path); } else if (mode === 2 && is_absolute_path(path)) path = get_relative_path(base_path, path, 1); return path; } _.get_file_path = get_file_path; // get_file_name[generateCode.dLK]='get_file_path,library_namespace.env.path_separator'; /** * 傳回檔名部分,the return value include ? # of URI<br /> * * @param {String}path * 路徑 * @param {String}[base_path] * base path * @param {Number}[mode] * 0:檔名,1:(當輸入為不可信賴的字串時)去除檔名中不允許的字元,割掉? #等 * * @returns 檔名部分 */ function get_file_name(path, base_path, mode) { path = get_file_path(path, 0, base_path); // 比起(mode=path.lastIndexOf(library_namespace.env.path_separator))==-1?path:path.substr(mode+1);此法比較直接,不過感覺多一道手續… path = path // 不能用.substr(path.lastIndexOf(library_namespace.env.path_separator))+library_namespace.env.path_separator,因為path.lastIndexOf(library_namespace.env.path_separator)可能==-1 .slice(path.lastIndexOf(library_namespace.env.path_separator) + 1); if (mode) { if (path.match(/[#?]/)) path = path.substr(0, RegExp.lastIndex - 1); // 處理 illegal file name. 去除檔名中不被允許的字元。cf. // application.net.to_file_name() // [ \.] path = path.replace(/[\\\/:*?"<>|]/g, '_'); } return path; } _.get_file_name = get_file_name; // 傳回檔案/資料夾物件 // FileSystemObjectのバグ(制限)で、環境によっては2G以上の領域を認識できません。WSH5.6ではこのバグが修正されています。 // get_file_object[generateCode.dLK]='isFile,parse_shortcut,get_file_path,library_namespace.env.path_separator,getFolder,initialization_WScript_Objects'; // path,mode=0:auto(維持原狀),1:絕對路徑,2:相對路徑,base function get_file_object(p, m, bp) { // path try { return isFile(p = parse_shortcut(get_file_path(p, m, bp), 1)) ? fso .GetFile(p) : fso.GetFolder(p); } catch (e) { return p.indexOf(library_namespace.env.path_separator) === -1 ? get_file_object( getFolder(WScript.ScriptFullName) + p, m, bp) : null; } } // alert(get_file_path('\program files\\xxx\\xxx.exe',2)); _// JSDT:_module_ . /** * 取得 file 之 file name extension(副檔名). * * @example <code> * // get 'htm'. * get_file_extension('test.htm'); * </code> * @param {String}file_name * 檔案名稱 * @returns {String} file name extension(副檔名) */ get_file_extension = function(file_name) { var m = ('' + file_name).match(/\.([^.]*)$/); return m ? m[1] : ''; }; _// JSDT:_module_ . /** * 設定/更改 file 之 extension(副檔名). * * @example <code> * // get 'test.html'. * set_file_extension('test.htm','html'); * </code> * @param {String}file_name * 檔案名稱 * @param {String}change_to * 更改成 change_to * @returns {String} 更改 extension 後之 file name. */ set_file_extension = function(file_name, change_to) { if (change_to) file_name = ('' + file_name).replace(/\.[^.]*$/, '') + '.' + change_to; return file_name; }; // http://stackoverflow.com/questions/1547899/which-characters-make-a-url-invalid // URL_encoded('http://authority.ddbc.edu.tw/time/search.php?chk=nextLevel&julianSwitch=off&dpk=新羅') // === false; function URL_encoded(URL) { return /^(?:https?|ftp):\/\/(?:[a-zA-Z0-9\-._~\/?#\[\]@!$&'()*+,;=]|%[a-fA-Z0-9]{2})+$/ .test(URL); } /** * cache 相關函數: * * @see application.storage.file.get_cache_file * application.OS.Windows.file.cacher * application.net.Ajax.get_URL_cache application.net.wiki * wiki_API.cache() CeL.wiki.cache() */ var get_file = library_namespace.get_file, // @see https://nodejs.org/api/fs.html write_file = library_namespace.platform.nodejs && require('fs').writeFileSync; /** * 檔案 cache 作業操作之輔助套裝函數。 * * @param {String|Object}URL * 請求目的URL or options * @param {Object}[options] * 附加參數/設定特殊功能與選項 * * @returns cache data */ function cache_file(URL, options) { // 前置處理。 if (!library_namespace.is_Object(options)) options = Object.create(null); // 決定將 URL 存放至何處(存成哪個檔案)。 var file_path = options.file_name || get_file_name; if (typeof file_path === 'function') file_path = file_path(URL); if (options.base_path) file_path = options.base_path + file_path; if (!URL_encoded(URL)) URL = encodeURI(URL); var file_encoding = options.file_encoding || options.encoding || cache_file.encoding, data; if (!options.force) { try { // get cache file data = get_file(file_path, file_encoding); // data = read_file(file_name, encoding); } catch (e) { library_namespace.debug('嘗試讀取 cache file [' + file_path + '] 失敗。', 1, 'cache_file'); } } if (data === undefined) { library_namespace.debug('重新取得 [' + URL + ']。', 1, 'cache_file'); if ((data = get_file(URL, options.encoding)) // 預防此時尚未 loaded. && (write_file || (write_file = library_namespace.write_file))) { if (/\.s?html?$/i.test(file_path)) { // 插入網域識別標記之網頁註解 MOTW (Mark of the Web)。 // e.g., about:internet // http://msdn.microsoft.com/en-us/library/ms537628(v=vs.85).aspx var MOTW = String(URL.length), line_separator = data .match(/\r?\n/); MOTW = '<!-- saved from url=(' + '0000'.slice(MOTW.length) + MOTW + ')' + URL + ' -->' + (line_separator ? line_separator[0] : library_namespace.env.line_separator); data = MOTW + data; } library_namespace.debug('write to [' + file_path + ']'); write_file(file_path, data, file_encoding); } } if (/\.json$/i.test(file_path) && JSON.parse) data = JSON.parse(data); return data; } _.cache_file = cache_file; /** {String}預設 file encoding for fs of write_file。 */ cache_file.encoding = library_namespace.platform.nodejs ? 'utf8' : 'UTF-8'; // --------------------------------------------------------------- // 可以參考 CeL.application.net.MIME // https://github.com/sindresorhus/file-type/blob/master/index.js function to_magic_array(header) { return header.split('').map(function(char) { return char.charCodeAt(0).toString(16); }).join(''); } function uint32_from_buffer(buffer, byteOffset) { // NG: (new Uint32Array(buffer, 4, 4))[0] byteOffset |= 0; return (((buffer[byteOffset + 3] * 256) + buffer[byteOffset + 2]) * 256 + buffer[byteOffset + 1]) * 256 + buffer[byteOffset]; } // Magic_number[type]={Object}data var Magic_number = { // text html : { // other file extensions extensions : [ 'htm', 'shtml' ], min_size : 52 }, pdf : { magic : to_magic_array('%PDF'), min_size : 800 }, // image gif : { // Header: GIF89a magic : to_magic_array('GIF89a'), // end of image data + GIF file terminator eof : '00 3B', min_size : 800 }, gif87a : { // Header: GIF87a magic : to_magic_array('GIF87a'), // end of image data + GIF file terminator eof : '00 3B', extensions : [ 'gif' ], extensions_without_type : true, min_size : 800 }, // PNG圖像 // https://www.w3.org/TR/PNG/#11IEND // https://en.wikipedia.org/wiki/PNG // https://en.wikipedia.org/wiki/APNG png : { // IHDR數據塊: ‰PNG.... magic : '89 50 4E 47 0D 0A 1A 0A', // http://blog.xuite.net/tzeng015/twblog/113272013-5.2.3+分析PNG圖像文件結構(6) // IEND數據塊: IEND®B`‚... eof : '00 00 00 00 49 45 4E 44 AE 42 60 82', min_size : 100 }, jpg : { // ÿØ magic : 'FF D8 FF', // http://stackoverflow.com/questions/4585527/detect-eof-for-jpg-images // check EOI, End Of Image mark of .jpeg: ÿÙ eof : 'FF D9', // other file extensions extensions : [ 'jpeg' ], min_size : 3e3 }, tif : { // II*. magic : '49 49 2A 00', // other file extensions extensions : [ 'tiff' ], min_size : 3e3 }, ico : { magic : '00 00 01 00', min_size : 50 }, // https://www.garykessler.net/library/file_sigs.html // archives zip : { // PK.. PKZIP壓縮軟體的發明者 Phil Katz,ZIP原名Deflate magic : '50 4B 03 04', min_size : 100 }, '7z' : { // 7z¼¯'. magic : '37 7A BC AF 27 1C', min_size : 100 }, rar : { // Rar!... magic : '52 61 72 21 1A 07 00', min_size : 100 }, rar5 : { // Rar!.... magic : '52 61 72 21 1A 07 01 00', min_size : 100 }, // sound mp3 : { // 49 44 33 04 00 magic : to_magic_array('ID3'), min_size : 100 }, // video mpg : { // 00 00 01 Bx magic : '00 00 01', eof : '00 00 01 B7', extensions : [ 'mpeg' ], min_size : 1e3 }, // RIFF types: 會自動判別RIFF標頭,不用特別指定。 wav : { min_size : 100, verify : function(file_contents) { // TODO: file_contents.slice(4, 8): File Size (little endian) return file_contents.slice(8, 15).toString() === 'WAVEfmt'; } }, avi : { min_size : 100, verify : function(file_contents) { // TODO: file_contents.slice(4, 8): File Size (little endian) return file_contents.slice(8, 16).toString() === 'AVI LIST'; } }, webp : { min_size : 100, // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header // https://en.wikipedia.org/wiki/WebP verify : function(file_contents) { // TODO: file_contents.slice(4, 8): File Size (little endian) // "WEBPVP8 " for lossy images, // "WEBPVP8L" for lossless images. return file_contents.slice(8, 15).toString() === 'WEBPVP8'; } } }, // Magic_number_data[byte count]=[{Natural}Magic_number:{Object}data] Magic_number_data = [], MAX_magic_byte_count = Math .log2(Number.MAX_SAFE_INTEGER) / 8 | 0; Object.entries(Magic_number).forEach(function(type) { var magic_data = type[1]; if (typeof magic_data === 'string') { // e.g., jpg:'FF D8 FF' magic_data = { magic : magic_data }; } // 檔案格式。 magic_data.type = type = type[0]; // 副檔名。 // TODO: 大小寫。 if (magic_data.extensions) { if (!Array.isArray(magic_data.extensions)) { magic_data.extensions = [ magic_data.extensions ]; } if (!magic_data.extensions_without_type // && !magic_data.extensions.includes(type)) { // 以 type name 為主 magic_data.extensions.unshift(type); } } else { magic_data.extensions = [ type ]; } if (!magic_data.magic) { // 不採用 Magic number 測試 return; } magic_data.magic = magic_data.magic.replace(/[ ,]+/g, ''); var magic_byte_count = magic_data.magic_byte_count // = magic_data.magic.length / 2 | 0, magic; if (magic_byte_count > MAX_magic_byte_count) { magic_byte_count = MAX_magic_byte_count; magic = magic_data.magic.slice(0, magic_byte_count * 2); } else { magic = magic_data.magic; } if (!Magic_number_data[magic_byte_count]) { Magic_number_data[magic_byte_count] = []; } Magic_number_data[magic_byte_count][parseInt(magic, 0x10)] // = magic_data; if (magic_data.eof) { magic_data.eof = magic_data.eof.replace(/[ ,]+/g, ''); magic_data.reversed_eof_bytes // = magic_data.eof.chunk(2).map(function(hex) { return parseInt(hex, 0x10); }).reverse(); if (false) { console.log(magic_data.type + ': eof: ' + magic_data.eof); console.log(magic_data.reversed_eof_bytes); } } }); /** * detect / 驗證檔案格式。 * * @param {Buffer}file_contents * file contents * @param {Object|String}options * 附加參數/設定選擇性/特殊功能與選項 * * @returns * * @since 2017/4/10 */ function verify_file_type(file_contents, options) { if (typeof options === 'string') { options = { type : options }; } else { // 前置作業。 options = library_namespace.setup_options(options); } var magic_data, damaged; // console.log(Magic_number_data); for (var i = 0, magic_number = 0; i < Math.min( Magic_number_data.length, MAX_magic_byte_count);) { magic_number = magic_number * 0x100 + file_contents[i]; // console.log(magic_number); if (Magic_number_data[++i] && (magic_data = Magic_number_data[i][magic_number])) { break; } } if (!magic_data && file_contents.slice(0, 4).toString() === 'RIFF') { // https://en.wikipedia.org/wiki/Resource_Interchange_File_Format // https://en.wikipedia.org/wiki/WebP var RIFF_type = file_contents.slice(8, 12).toString(), // an unsigned, little-endian 32-bit integer with the length of // this chunk (except this field itself and the chunk // identifier). chunk_size = uint32_from_buffer(file_contents, 4); library_namespace.debug('verify_file_type: RIFF type: ' + RIFF_type + ', ' + chunk_size + ' bytes, stream length: ' + file_contents.length); // console.log(file_contents.slice(0, 24)); magic_data = Magic_number[RIFF_type.toLowerCase()]; if (file_contents.length - chunk_size !== 8) { // file damaged damaged = true; } else if (magic_data && (typeof magic_data.verify === 'function')) { damaged = !magic_data.verify(file_contents); } } if (!magic_data) { var contents_String = file_contents.toString(); // e.g., "<!DOCTYPE html>", "<!DOCTYPE html PUBLIC "... if (contents_String.trimStart().startsWith('<!DOCTYPE html') // TODO: 此處為太過簡陋的測試 || /<html[ a-z\d-="']*>/i.test(contents_String)) { magic_data = Magic_number.html; } } if (!magic_data) { // 無法判別。 if (options.type in Magic_number) { return { verified : false }; } return {}; } var result = { type : magic_data.type, // 副檔名。 extension : magic_data.extensions[0], extensions : magic_data.extensions }; if (magic_data.min_size > 0) { result.too_small = file_contents.length < magic_data.min_size; } if (damaged) { // file damaged result.damaged = true; } else if (magic_data.reversed_eof_bytes) { // verify 檔案損毀 var file_index = file_contents.length; if (magic_data.reversed_eof_bytes.some(function(byte, index) { if (false) { console.log('magic: ' + byte + ' vs. ' + file_contents[file_index - 1]); } return byte !== file_contents[--file_index]; })) { // file damaged result.damaged = true; } } if (options.type) { // TODO: check 副檔名 result.verified = options.type === result.type; } return result; } _.file_type = verify_file_type; // --------------------------------------------------------------- return (_// JSDT:_module_ ); }