├── assets ├── mimetype ├── container.xml ├── toc.ejs ├── content.ejs └── Style.css ├── .gitignore ├── package.json ├── README.md ├── uuid.js └── jianhelper.js /assets/mimetype: -------------------------------------------------------------------------------- 1 | application/epub+zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | out 3 | out.epub -------------------------------------------------------------------------------- /assets/container.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jianhelper", 3 | "version": "1.0.0", 4 | "description": "A downloader for articles in jianshu.com.", 5 | "main": "jianhelper.js", 6 | "author": "wizardforcel", 7 | "license": "MIT", 8 | "dependencies": { 9 | "cheerio": "^0.19.0", 10 | "ejs": "^2.4.1", 11 | "jszip": "^2.5.0", 12 | "sync-request": "^2.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 简书助手 2 | 3 | 爬取简书的文章,并生成EPUB格式。 4 | 5 | ## 用法 6 | 7 | 首先到[官网](https://nodejs.org/en/download/)下载并安装node.js。 8 | 9 | ``` 10 | git clone https://github.com/wizardforcel/jianhelper.git 11 | cd jianhelper 12 | npm install 13 | node jianhelper url [start [end]] 14 | ``` 15 | 16 | url:支持三种类型 17 | 18 | + http://www.jianshu.com/users/{id} 用户 19 | + http://www.jianshu.com/notebooks/{id} 文集 20 | + http://www.jianshu.com/collection/{id} 专题 21 | 22 | start:起始页,默认为第一页 23 | 24 | end:终止页,默认为最后一页 25 | 26 | ## TODO 27 | 28 | + 添加标题、封面 29 | 30 | ## 协议 31 | 32 | MIT-LICENSE 33 | -------------------------------------------------------------------------------- /assets/toc.ejs: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Unknown 12 | 13 | 14 | <% for(var i = 0; i < toc.length; i++) {%> 15 | 16 | 17 | <%- toc[i].title %> 18 | 19 | 20 | 21 | <% } %> 22 | 23 | -------------------------------------------------------------------------------- /assets/content.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | urn:uuid:<%- uuid %> 5 | 6 | zh-CN 7 | <%- date %> 8 | 9 | 10 | 11 | <% for(var i = 0; i < toc.length; i++) {%> 12 | 13 | <% } %> 14 | 15 | 16 | <% for(var i = 0; i < toc.length; i++) {%> 17 | 18 | <% } %> 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /uuid.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Math.uuid.js (v1.4) 3 | 4 | http://www.broofa.com 5 | 6 | mailto:robert@broofa.com 7 | 8 | Copyright (c) 2010 Robert Kieffer 9 | Dual licensed under the MIT and GPL licenses. 10 | */ 11 | 12 | /* 13 | * Generate a random uuid. 14 | * 15 | * USAGE: Math.uuid(length, radix) 16 | * length - the desired number of characters 17 | * radix - the number of allowable values for each character. 18 | * 19 | * EXAMPLES: 20 | * // No arguments - returns RFC4122, version 4 ID 21 | * >>> Math.uuid() 22 | * "92329D39-6F5C-4520-ABFC-AAB64544E172" 23 | * 24 | * // One argument - returns ID of the specified length 25 | * >>> Math.uuid(15) // 15 character ID (default base=62) 26 | * "VcydxgltxrVZSTV" 27 | * 28 | * // Two arguments - returns ID of the specified length, and radix. (Radix must be <= 62) 29 | * >>> Math.uuid(8, 2) // 8 character ID (base=2) 30 | * "01001010" 31 | * >>> Math.uuid(8, 10) // 8 character ID (base=10) 32 | * "47473046" 33 | * >>> Math.uuid(8, 16) // 8 character ID (base=16) 34 | * "098F4D35" 35 | */ 36 | 37 | var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); 38 | 39 | exports.uuid = function (len, radix) { 40 | var chars = CHARS, uuid = [], i; 41 | radix = radix || chars.length; 42 | 43 | if (len) { 44 | // Compact form 45 | for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix]; 46 | } else { 47 | // rfc4122, version 4 form 48 | var r; 49 | 50 | // rfc4122 requires these characters 51 | uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; 52 | uuid[14] = '4'; 53 | 54 | // Fill in random data. At i==19 set the high bits of clock sequence as 55 | // per rfc4122, sec. 4.1.5 56 | for (i = 0; i < 36; i++) { 57 | if (!uuid[i]) { 58 | r = 0 | Math.random()*16; 59 | uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; 60 | } 61 | } 62 | } 63 | 64 | return uuid.join(''); 65 | }; 66 | 67 | // A more performant, but slightly bulkier, RFC4122v4 solution. We boost performance 68 | // by minimizing calls to random() 69 | exports.uuidFast = function() { 70 | var chars = CHARS, uuid = new Array(36), rnd=0, r; 71 | for (var i = 0; i < 36; i++) { 72 | if (i==8 || i==13 || i==18 || i==23) { 73 | uuid[i] = '-'; 74 | } else if (i==14) { 75 | uuid[i] = '4'; 76 | } else { 77 | if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0; 78 | r = rnd & 0xf; 79 | rnd = rnd >> 4; 80 | uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; 81 | } 82 | } 83 | return uuid.join(''); 84 | }; 85 | 86 | // A more compact, but less performant, RFC4122v4 solution: 87 | exports.uuidCompact = function() { 88 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 89 | var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 90 | return v.toString(16); 91 | }); 92 | }; -------------------------------------------------------------------------------- /assets/Style.css: -------------------------------------------------------------------------------- 1 | /* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */ 2 | /* Author: Nicolas Hery - http://nicolashery.com */ 3 | /* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */ 4 | /* Source: https://github.com/nicolahery/markdownpad-github */ 5 | 6 | /* RESET 7 | =============================================================================*/ 8 | 9 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { 10 | margin: 0; 11 | padding: 0; 12 | border: 0; 13 | } 14 | 15 | /* BODY 16 | =============================================================================*/ 17 | 18 | body { 19 | font-family: Helvetica, arial, freesans, clean, sans-serif; 20 | font-size: 14px; 21 | line-height: 1.6; 22 | color: #333; 23 | background-color: #fff; 24 | padding: 20px; 25 | max-width: 960px; 26 | margin: 0 auto; 27 | } 28 | 29 | body>*:first-child { 30 | margin-top: 0 !important; 31 | } 32 | 33 | body>*:last-child { 34 | margin-bottom: 0 !important; 35 | } 36 | 37 | /* BLOCKS 38 | =============================================================================*/ 39 | 40 | p, blockquote, ul, ol, dl, table, pre { 41 | margin: 15px 0; 42 | } 43 | 44 | /* HEADERS 45 | =============================================================================*/ 46 | 47 | h1, h2, h3, h4, h5, h6 { 48 | margin: 20px 0 10px; 49 | padding: 0; 50 | font-weight: bold; 51 | -webkit-font-smoothing: antialiased; 52 | } 53 | 54 | h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code { 55 | font-size: inherit; 56 | } 57 | 58 | h1 { 59 | font-size: 24px; 60 | border-bottom: 1px solid #ccc; 61 | color: #000; 62 | } 63 | 64 | h2 { 65 | font-size: 18px; 66 | color: #000; 67 | } 68 | 69 | h3 { 70 | font-size: 14px; 71 | } 72 | 73 | h4 { 74 | font-size: 14px; 75 | } 76 | 77 | h5 { 78 | font-size: 14px; 79 | } 80 | 81 | h6 { 82 | color: #777; 83 | font-size: 14px; 84 | } 85 | 86 | body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child { 87 | margin-top: 0; 88 | padding-top: 0; 89 | } 90 | 91 | a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 { 92 | margin-top: 0; 93 | padding-top: 0; 94 | } 95 | 96 | h1+p, h2+p, h3+p, h4+p, h5+p, h6+p { 97 | margin-top: 10px; 98 | } 99 | 100 | /* LINKS 101 | =============================================================================*/ 102 | 103 | a { 104 | color: #4183C4; 105 | text-decoration: none; 106 | } 107 | 108 | a:hover { 109 | text-decoration: underline; 110 | } 111 | 112 | /* LISTS 113 | =============================================================================*/ 114 | 115 | ul, ol { 116 | padding-left: 30px; 117 | } 118 | 119 | ul li > :first-child, 120 | ol li > :first-child, 121 | ul li ul:first-of-type, 122 | ol li ol:first-of-type, 123 | ul li ol:first-of-type, 124 | ol li ul:first-of-type { 125 | margin-top: 0px; 126 | } 127 | 128 | ul ul, ul ol, ol ol, ol ul { 129 | margin-bottom: 0; 130 | } 131 | 132 | dl { 133 | padding: 0; 134 | } 135 | 136 | dl dt { 137 | font-size: 14px; 138 | font-weight: bold; 139 | font-style: italic; 140 | padding: 0; 141 | margin: 15px 0 5px; 142 | } 143 | 144 | dl dt:first-child { 145 | padding: 0; 146 | } 147 | 148 | dl dt>:first-child { 149 | margin-top: 0px; 150 | } 151 | 152 | dl dt>:last-child { 153 | margin-bottom: 0px; 154 | } 155 | 156 | dl dd { 157 | margin: 0 0 15px; 158 | padding: 0 15px; 159 | } 160 | 161 | dl dd>:first-child { 162 | margin-top: 0px; 163 | } 164 | 165 | dl dd>:last-child { 166 | margin-bottom: 0px; 167 | } 168 | 169 | /* CODE 170 | =============================================================================*/ 171 | 172 | pre, code, tt { 173 | font-size: 12px; 174 | font-family: Consolas, "Liberation Mono", Courier, monospace; 175 | } 176 | 177 | code, tt { 178 | margin: 0 0px; 179 | padding: 0px 0px; 180 | white-space: nowrap; 181 | border: 1px solid #eaeaea; 182 | background-color: #f8f8f8; 183 | border-radius: 3px; 184 | } 185 | 186 | pre>code { 187 | margin: 0; 188 | padding: 0; 189 | white-space: pre; 190 | border: none; 191 | background: transparent; 192 | } 193 | 194 | pre { 195 | background-color: #f8f8f8; 196 | border: 1px solid #ccc; 197 | font-size: 13px; 198 | line-height: 19px; 199 | overflow: auto; 200 | padding: 6px 10px; 201 | border-radius: 3px; 202 | } 203 | 204 | pre code, pre tt { 205 | background-color: transparent; 206 | border: none; 207 | } 208 | 209 | kbd { 210 | -moz-border-bottom-colors: none; 211 | -moz-border-left-colors: none; 212 | -moz-border-right-colors: none; 213 | -moz-border-top-colors: none; 214 | background-color: #DDDDDD; 215 | background-image: linear-gradient(#F1F1F1, #DDDDDD); 216 | background-repeat: repeat-x; 217 | border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD; 218 | border-image: none; 219 | border-radius: 2px 2px 2px 2px; 220 | border-style: solid; 221 | border-width: 1px; 222 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; 223 | line-height: 10px; 224 | padding: 1px 4px; 225 | } 226 | 227 | /* QUOTES 228 | =============================================================================*/ 229 | 230 | blockquote { 231 | border-left: 4px solid #DDD; 232 | padding: 0 15px; 233 | color: #777; 234 | } 235 | 236 | blockquote>:first-child { 237 | margin-top: 0px; 238 | } 239 | 240 | blockquote>:last-child { 241 | margin-bottom: 0px; 242 | } 243 | 244 | /* HORIZONTAL RULES 245 | =============================================================================*/ 246 | 247 | hr { 248 | clear: both; 249 | margin: 15px 0; 250 | height: 0px; 251 | overflow: hidden; 252 | border: none; 253 | background: transparent; 254 | border-bottom: 4px solid #ddd; 255 | padding: 0; 256 | } 257 | 258 | /* TABLES 259 | =============================================================================*/ 260 | 261 | table th { 262 | font-weight: bold; 263 | } 264 | 265 | table th, table td { 266 | border: 1px solid #ccc; 267 | padding: 6px 13px; 268 | } 269 | 270 | table tr { 271 | border-top: 1px solid #ccc; 272 | background-color: #fff; 273 | } 274 | 275 | table tr:nth-child(2n) { 276 | background-color: #f8f8f8; 277 | } 278 | 279 | /* IMAGES 280 | =============================================================================*/ 281 | 282 | img { 283 | max-width: 100% 284 | } 285 | 286 | /* FOR JIANSHU 287 | =============================================================================*/ 288 | 289 | .image-package { 290 | text-align: center 291 | } -------------------------------------------------------------------------------- /jianhelper.js: -------------------------------------------------------------------------------- 1 | var cheerio = require('cheerio'); 2 | var request = require('sync-request'); 3 | var fs = require('fs'); 4 | var process = require('process'); 5 | var ejs = require('ejs'); 6 | var jszip = require('jszip'); 7 | 8 | var types = { 9 | unknown: 0, 10 | user: 1, 11 | notebook: 2, 12 | collection: 3 13 | } 14 | 15 | var selectors = { 16 | article: '.title a', 17 | title: 'h1.title', 18 | author: 'a.author-name span', 19 | content: '.show-content' 20 | }; 21 | 22 | var url = process.argv[2]; 23 | if(!url) 24 | { 25 | showUsage(); 26 | process.exit(0); 27 | } 28 | 29 | var startPage = process.argv[3]; 30 | startPage = startPage? Number.parseInt(startPage): 1; 31 | 32 | var endPage = process.argv[4]; 33 | endPage = endPage? Number.parseInt(endPage): Number.POSITIVE_INFINITY; 34 | 35 | 36 | var urlResult = checkUrl(url); 37 | if(urlResult.type == types.unknown) 38 | { 39 | showUsage(); 40 | process.exit(0); 41 | } 42 | 43 | var url = getRealUrl(urlResult.type, urlResult.id, url); 44 | var toc = []; 45 | 46 | console.log('Init path...'); 47 | initPath(); 48 | 49 | for(var i = startPage; i <= endPage; i++) 50 | { 51 | console.log('page: ' + i.toString()) 52 | var pageUrl = url.replace(/\{page\}/, i.toString()); 53 | var html; 54 | try { 55 | html = request('GET', pageUrl).getBody().toString(); 56 | } catch(ex) { break; } 57 | var li = getList(html); 58 | if(li.length == 0) 59 | break; 60 | for(var j in li) 61 | { 62 | try { 63 | var artUrl = li[j]; 64 | var fname = /\/p\/(\w{12})/.exec(artUrl)[1] + '.html'; 65 | var html = request('GET', artUrl).getBody().toString(); 66 | var co = getContent(html); 67 | fs.writeFileSync('./out/OEBPS/Text/' + fname, co, {encoding: 'utf-8'}); 68 | var title = /

(.+?)<\/h1>/.exec(co)[1]; 69 | toc.push({file: fname, title: title}); 70 | console.log('article: ' + fname + ', title: ' + title); 71 | } catch(ex) { console.log(ex.toString()); } 72 | } 73 | } 74 | 75 | var uuidGenerator = require('./uuid.js'); 76 | var uuid = uuidGenerator.uuid(); 77 | 78 | console.log('Generate content.opf...') 79 | var contentOpf = getContentOpf(toc, uuid); 80 | fs.writeFileSync('./out/OEBPS/content.opf', contentOpf, {encoding: 'utf-8'}) 81 | 82 | console.log('Generate toc.ncx...') 83 | var tocNcx = getTocNcx(toc, uuid); 84 | fs.writeFileSync('./out/OEBPS/toc.ncx', tocNcx, {encoding: 'utf-8'}) 85 | 86 | console.log('Gnerate epub...'); 87 | generate(); 88 | 89 | console.log('Done..'); 90 | 91 | function showUsage() { 92 | var usage = "用法:node jianhelper url [start [end]]\n\n" + 93 | " url:支持三种类型\n\n" + 94 | " http://www.jianshu.com/users/{id} 用户\n" + 95 | " http://www.jianshu.com/notebooks/{id} 文集\n" + 96 | " http://www.jianshu.com/collection/{id} 专题\n\n" + 97 | " start:起始页,默认为第一页\n\n" + 98 | " end:终止页,默认为最后一页"; 99 | console.log(usage); 100 | } 101 | 102 | function checkUrl(url) 103 | { 104 | var regexes = { 105 | user: /^https?:\/\/www\.jianshu\.com\/users\/(\w{6,12})\/?$/, 106 | notebook: /^https?:\/\/www\.jianshu\.com\/notebooks\/(\d+)\/?$/, 107 | collection: /^https?:\/\/www\.jianshu\.com\/collection\/(\w{6,12})\/?$/ 108 | }; 109 | 110 | var type = types.unknown; 111 | var id; 112 | 113 | for(var k in regexes) 114 | { 115 | var rms; 116 | if(rms = regexes[k].exec(url)) 117 | { 118 | type = types[k]; 119 | id = rms[1]; 120 | break; 121 | } 122 | } 123 | 124 | return {type: type, id: id}; 125 | } 126 | 127 | function getRealUrl(type, id, url) 128 | { 129 | if(type == types.user) 130 | { 131 | return 'http://www.jianshu.com/users/' + id + 132 | '/latest_articles?page={page}'; 133 | } 134 | else if(type == types.notebook) 135 | { 136 | return 'http://www.jianshu.com/notebooks/' + id + 137 | '/latest?page={page}'; 138 | } 139 | else if(type == types.collection) 140 | { 141 | var content = request('GET', url).getBody().toString();; 142 | var realId = /href="\/collections\/(\d+)\//.exec(content)[1]; 143 | return 'http://www.jianshu.com/collections/' + realId + 144 | '/notes?order_by=added_at&page={page}'; 145 | } 146 | } 147 | 148 | function getList(html) { 149 | 150 | var $ = cheerio.load(html); 151 | 152 | var $list = $(selectors.article); 153 | var res = []; 154 | for(var i = 0; i < $list.length; i++) 155 | { 156 | var url = $list.eq(i).attr('href'); 157 | if(!url.startsWith('http')) 158 | url = 'http://www.jianshu.com' + url; 159 | res.push(url); 160 | } 161 | return res; 162 | } 163 | 164 | 165 | function getContent(html) { 166 | var $ = cheerio.load(html); 167 | dealWithImg($); 168 | 169 | var header = '\r\n\r\n\r\n' + 170 | '' + 171 | '\r\n\r\n'; 172 | 173 | var title = '

' + $(selectors.title).text() + '

'; 174 | var author = '

作者:' + $(selectors.author).text() + '

'; 175 | var content = $(selectors.content).html(); 176 | 177 | var footer = '\r\n\r\n'; 178 | 179 | return header + title + '\n' + author + '\n' + content + footer; 180 | } 181 | 182 | function initPath() 183 | { 184 | if(fs.existsSync('./out') && 185 | fs.statSync('./out').isDirectory()) 186 | rRmDir('./out'); 187 | fs.mkdirSync('./out'); 188 | fs.mkdirSync('./out/OEBPS'); 189 | fs.mkdirSync('./out/OEBPS/Text'); 190 | fs.mkdirSync('./out/OEBPS/Images'); 191 | //try {fs.mkdirSync('./out/OEBPS/Styles');} catch(ex) {} 192 | //try {fs.mkdirSync('./out/META-INF');} catch(ex) {} 193 | //fs.writeFileSync('./out/META-INF/container.xml', fs.readFileSync('./assets/container.xml')); 194 | //fs.writeFileSync('./out/mimetype', fs.readFileSync('./assets/mimetype')); 195 | //fs.writeFileSync('./out/OEBPS/Styles/Style.css', fs.readFileSync('./assets/Style.css')); 196 | } 197 | 198 | function dealWithImg($) 199 | { 200 | var imgs = $(selectors.content + ' img'); 201 | for(var i = 0; i < imgs.length; i++) 202 | { 203 | try { 204 | var img = imgs.eq(i); 205 | var url = img.attr('src'); 206 | var co = request('GET', url).getBody(); 207 | var fname = /[\w\-]+\.(?:jpg|png|gif)/.exec(url)[0]; 208 | console.log('img: ' + fname); 209 | fs.writeFileSync('./out/OEBPS/Images/' + fname, co); 210 | img.attr('src', '../Images/' + fname); 211 | } catch(ex) { console.log(ex.toString()); } 212 | } 213 | } 214 | 215 | function getContentOpf(toc, uuid) 216 | { 217 | var date = new Date(); 218 | var dateStr 219 | = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(); 220 | 221 | return ejs.render(fs.readFileSync('assets/content.ejs', 'utf-8'), { 222 | date: dateStr, 223 | toc: toc, 224 | uuid: uuid 225 | }); 226 | } 227 | 228 | function getTocNcx(toc, uuid) 229 | { 230 | return ejs.render(fs.readFileSync('assets/toc.ejs', 'utf-8'), { 231 | toc: toc, 232 | uuid: uuid 233 | }); 234 | } 235 | 236 | function generate() 237 | { 238 | var zip = new jszip(); 239 | zip.file('mimetype', fs.readFileSync('./assets/mimetype')); 240 | zip.file('META-INF/container.xml', fs.readFileSync('./assets/container.xml')); 241 | zip.file('OEBPS/Styles/Style.css', fs.readFileSync('./assets/Style.css')); 242 | zip.file('OEBPS/content.opf', fs.readFileSync('./out/OEBPS/content.opf')); 243 | zip.file('OEBPS/toc.ncx', fs.readFileSync('./out/OEBPS/toc.ncx')); 244 | 245 | var articles = fs.readdirSync('./out/OEBPS/Text'); 246 | for(var i = 0; i < articles.length; i++) 247 | { 248 | var fname = articles[i]; 249 | zip.file('OEBPS/Text/' + fname, fs.readFileSync('./out/OEBPS/Text/' + fname)); 250 | } 251 | 252 | var images = fs.readdirSync('./out/OEBPS/Images'); 253 | for(var i = 0; i < images.length; i++) 254 | { 255 | var fname = images[i]; 256 | zip.file('OEBPS/Images/' + fname, fs.readFileSync('./out/OEBPS/Images/' + fname)); 257 | } 258 | 259 | fs.writeFileSync('out.epub', zip.generate({type: 'nodebuffer', 'compression':'DEFLATE'})); 260 | } 261 | 262 | function rRmDir(dir) 263 | { 264 | var flist = fs.readdirSync(dir); 265 | for(var i = 0; i < flist.length; i++) 266 | { 267 | var item = dir + '/' + flist[i]; 268 | if(fs.statSync(item).isDirectory()) 269 | rRmDir(item); 270 | else 271 | fs.unlinkSync(item); 272 | } 273 | fs.rmdirSync(dir); 274 | } --------------------------------------------------------------------------------