-
Notifications
You must be signed in to change notification settings - Fork 7
/
coverage.html
354 lines (307 loc) · 185 KB
/
coverage.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
<!DOCTYPE html><html><head><title>Coverage</title><script>
headings = [];
onload = function(){
headings = document.querySelectorAll('h2');
};
onscroll = function(e){
var heading = find(window.scrollY);
if (!heading) return;
var links = document.querySelectorAll('#menu a')
, link;
for (var i = 0, len = links.length; i < len; ++i) {
link = links[i];
link.className = link.getAttribute('href') == '#' + heading.id
? 'active'
: '';
}
};
function find(y) {
var i = headings.length
, heading;
while (i--) {
heading = headings[i];
if (y >= heading.offsetTop) {
return heading;
}
}
}
</script>
<style>
body {
font: 14px/1.6 "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 0;
color: #2C2C2C;
border-top: 2px solid #ddd;
}
#coverage {
padding: 60px;
}
h1 a {
color: inherit;
font-weight: inherit;
}
h1 a:hover {
text-decoration: none;
}
.onload h1 {
opacity: 1;
}
h2 {
width: 80%;
margin-top: 80px;
margin-bottom: 0;
font-weight: 100;
letter-spacing: 1px;
border-bottom: 1px solid #eee;
}
a {
color: #8A6343;
font-weight: bold;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
ul {
margin-top: 20px;
padding: 0 15px;
width: 100%;
}
ul li {
float: left;
width: 40%;
margin-top: 5px;
margin-right: 60px;
list-style: none;
border-bottom: 1px solid #eee;
padding: 5px 0;
font-size: 12px;
}
ul::after {
content: '.';
height: 0;
display: block;
visibility: hidden;
clear: both;
}
code {
font: 12px monaco, monospace;
}
pre {
margin: 30px;
padding: 30px;
border: 1px solid #eee;
border-bottom-color: #ddd;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
-webkit-box-shadow: inset 0 0 10px #eee;
-moz-box-shadow: inset 0 0 10px #eee;
box-shadow: inset 0 0 10px #eee;
overflow-x: auto;
}
img {
margin: 30px;
padding: 1px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
-moz-box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
max-width: 100%;
}
footer {
background: #eee;
width: 100%;
padding: 50px 0;
text-align: right;
border-top: 1px solid #ddd;
}
footer span {
display: block;
margin-right: 30px;
color: #888;
font-size: 12px;
}
#menu {
position: fixed;
font-size: 12px;
overflow-y: auto;
top: 0;
right: 0;
margin: 0;
height: 100%;
padding: 15px 0;
text-align: right;
border-left: 1px solid #eee;
-moz-box-shadow: 0 0 2px #888
, inset 5px 0 20px rgba(0,0,0,.5)
, inset 5px 0 3px rgba(0,0,0,.3);
-webkit-box-shadow: 0 0 2px #888
, inset 5px 0 20px rgba(0,0,0,.5)
, inset 5px 0 3px rgba(0,0,0,.3);
box-shadow: 0 0 2px #888
, inset 5px 0 20px rgba(0,0,0,.5)
, inset 5px 0 3px rgba(0,0,0,.3);
-webkit-font-smoothing: antialiased;
background: url("data:image/png;base64,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");
}
#menu::after {
display: block;
content: '';
padding-top: 80px;
}
#logo {
position: fixed;
bottom: 10px;
right: 10px;
background: rgba(255,255,255,.1);
font-size: 11px;
display: block;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
-webkit-border-radius: 20px;
-moz-border-radius: 20px;
border-radius: 20px;
-webkit-box-shadow: 0 0 3px rgba(0,0,0,.2);
-moz-box-shadow: 0 0 3px rgba(0,0,0,.2);
box-shadow: 0 0 3px rgba(0,0,0,.2);
color: inherit;
}
#menu li a {
display: block;
color: white;
padding: 0 35px 0 25px;
-webkit-transition: background 300ms;
-moz-transition: background 300ms;
}
#menu li {
position: relative;
list-style: none;
}
#menu a:hover,
#menu a.active {
text-decoration: none;
background: rgba(255,255,255,.1);
}
#menu li:hover .cov {
opacity: 1;
}
#menu li .dirname {
opacity: .60;
padding-right: 2px;
}
#menu li .basename {
opacity: 1;
}
#menu .cov {
background: rgba(0,0,0,.4);
position: absolute;
top: 0;
right: 8px;
font-size: 9px;
opacity: .6;
text-align: left;
width: 17px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
padding: 2px 3px;
text-align: center;
}
#stats:nth-child(2n) {
display: inline-block;
margin-top: 15px;
border: 1px solid #eee;
padding: 10px;
-webkit-box-shadow: inset 0 0 2px #eee;
-moz-box-shadow: inset 0 0 2px #eee;
box-shadow: inset 0 0 2px #eee;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
#stats div {
float: left;
padding: 0 5px;
}
#stats::after {
display: block;
content: '';
clear: both;
}
#stats .sloc::after {
content: ' SLOC';
color: #b6b6b6;
}
#stats .percentage::after {
content: ' coverage';
color: #b6b6b6;
}
#stats .hits,
#stats .misses {
display: none;
}
.high {
color: #00d4b4;
}
.medium {
color: #e87d0d;
}
.low {
color: #d4081a;
}
.terrible {
color: #d4081a;
font-weight: bold;
}
table {
width: 80%;
margin-top: 10px;
border-collapse: collapse;
border: 1px solid #cbcbcb;
color: #363636;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
table thead {
display: none;
}
table td.line,
table td.hits {
width: 20px;
background: #eaeaea;
text-align: center;
font-size: 11px;
padding: 0 10px;
color: #949494;
}
table td.hits {
width: 10px;
padding: 2px 5px;
color: rgba(0,0,0,.2);
background: #f0f0f0;
}
tr.miss td.line,
tr.miss td.hits {
background: #e6c3c7;
}
tr.miss td {
background: #f8d5d8;
}
td.source {
padding-left: 15px;
line-height: 15px;
white-space: pre;
font: 12px monaco, monospace;
}
code .comment { color: #ddd }
code .init { color: #2F6FAD }
code .string { color: #5890AD }
code .keyword { color: #8A6343 }
code .number { color: #2F6FAD }
</style></head><body><div id="coverage"><h1 id="overview">Coverage</h1><div id="menu"><li><a href="#overview">overview</a></li><li><span class="cov medium">73</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/fdfs.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">fdfs.js</span></a></li><li><span class="cov high">93</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/helpers.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">helpers.js</span></a></li><li><span class="cov high">100</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/logger.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">logger.js</span></a></li><li><span class="cov high">87</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/protocol.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">protocol.js</span></a></li><li><span class="cov high">91</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/storage.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">storage.js</span></a></li><li><span class="cov high">92</span><a href="#/Users/chenboxiang/WebstormProjects/fdfs-client/lib/tracker.js"><span class="dirname">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/</span><span class="basename">tracker.js</span></a></li><a id="logo" href="http://visionmedia.github.io/mocha/">m</a></div><div id="stats" class="high"><div class="percentage">86%</div><div class="sloc">566</div><div class="hits">490</div><div class="misses">76</div></div><div id="files"><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/fdfs.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/fdfs.js</h2><div id="stats" class="medium"><div class="percentage">73%</div><div class="sloc">144</div><div class="hits">106</div><div class="misses">38</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-13</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午8:42</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict'</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var _ = require('lodash')</td></tr><tr class="hit"><td class="line">9</td><td class="hits">1</td><td class="source">var Tracker = require('./tracker')</td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var EventEmitter = require('events').EventEmitter</td></tr><tr class="hit"><td class="line">11</td><td class="hits">1</td><td class="source">var util = require('util')</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">var logger = require('./logger')</td></tr><tr class="hit"><td class="line">13</td><td class="hits">1</td><td class="source">var fs = require('fs')</td></tr><tr class="hit"><td class="line">14</td><td class="hits">1</td><td class="source">var is = require('is-type-of')</td></tr><tr class="hit"><td class="line">15</td><td class="hits">1</td><td class="source">var path = require('path')</td></tr><tr class="hit"><td class="line">16</td><td class="hits">1</td><td class="source">var helpers = require('./helpers')</td></tr><tr class="hit"><td class="line">17</td><td class="hits">1</td><td class="source">var protocol = require('./protocol')</td></tr><tr><td class="line">18</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">19</td><td class="hits">1</td><td class="source">var defaults = {</td></tr><tr><td class="line">20</td><td class="hits"></td><td class="source"> charset: 'utf8',</td></tr><tr><td class="line">21</td><td class="hits"></td><td class="source"> trackers: [],</td></tr><tr><td class="line">22</td><td class="hits"></td><td class="source"> // 默认超时时间10s</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source"> timeout: 10000,</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source"> // 默认后缀</td></tr><tr><td class="line">25</td><td class="hits"></td><td class="source"> // 当获取不到文件后缀时使用</td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source"> defaultExt: 'txt'</td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">28</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">29</td><td class="hits">1</td><td class="source">function FdfsClient(config) {</td></tr><tr class="hit"><td class="line">30</td><td class="hits">1</td><td class="source"> EventEmitter.call(this)</td></tr><tr><td class="line">31</td><td class="hits"></td><td class="source"> // config global logger</td></tr><tr class="hit"><td class="line">32</td><td class="hits">1</td><td class="source"> if (config.logger) {</td></tr><tr class="hit"><td class="line">33</td><td class="hits">1</td><td class="source"> logger.setLogger(config.logger)</td></tr><tr><td class="line">34</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">35</td><td class="hits">1</td><td class="source"> this.config = _.extend({}, defaults, config)</td></tr><tr><td class="line">36</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">37</td><td class="hits">1</td><td class="source"> this._checkConfig()</td></tr><tr class="hit"><td class="line">38</td><td class="hits">1</td><td class="source"> this._init()</td></tr><tr class="hit"><td class="line">39</td><td class="hits">1</td><td class="source"> this._errorHandle()</td></tr><tr><td class="line">40</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">41</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">42</td><td class="hits"></td><td class="source">// extends from EventEmitter</td></tr><tr class="hit"><td class="line">43</td><td class="hits">1</td><td class="source">util.inherits(FdfsClient, EventEmitter)</td></tr><tr><td class="line">44</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">45</td><td class="hits"></td><td class="source">// ------------- private methods</td></tr><tr><td class="line">46</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">47</td><td class="hits"></td><td class="source"> * 确认配置是否合法</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">49</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">50</td><td class="hits">1</td><td class="source">FdfsClient.prototype._checkConfig = function() {</td></tr><tr><td class="line">51</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source"> // ------------- 验证trackers是否合法</td></tr><tr class="hit"><td class="line">53</td><td class="hits">1</td><td class="source"> if (!this.config.trackers) {</td></tr><tr class="miss"><td class="line">54</td><td class="hits">0</td><td class="source"> throw new Error('you must specify "trackers" in config.')</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">57</td><td class="hits">1</td><td class="source"> if (!Array.isArray(this.config.trackers)) {</td></tr><tr class="miss"><td class="line">58</td><td class="hits">0</td><td class="source"> this.config.trackers = [this.config.trackers]</td></tr><tr><td class="line">59</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">61</td><td class="hits">1</td><td class="source"> if (this.config.trackers.length === 0) {</td></tr><tr class="miss"><td class="line">62</td><td class="hits">0</td><td class="source"> throw new Error('"trackers" in config is empty.')</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">64</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">65</td><td class="hits">1</td><td class="source"> this.config.trackers.forEach(function(tracker) {</td></tr><tr class="hit"><td class="line">66</td><td class="hits">1</td><td class="source"> if (!tracker.host || !tracker.port) {</td></tr><tr class="miss"><td class="line">67</td><td class="hits">0</td><td class="source"> throw new Error('"trackers" in config is invalid, every tracker must all have "host" and "port".')</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">69</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">71</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">72</td><td class="hits">1</td><td class="source">FdfsClient.prototype._init = function() {</td></tr><tr><td class="line">73</td><td class="hits"></td><td class="source"> // --------- init trackers</td></tr><tr class="hit"><td class="line">74</td><td class="hits">1</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">75</td><td class="hits">1</td><td class="source"> this._trackers = []</td></tr><tr class="hit"><td class="line">76</td><td class="hits">1</td><td class="source"> this.config.trackers.forEach(function(tc) {</td></tr><tr class="hit"><td class="line">77</td><td class="hits">1</td><td class="source"> tc.timeout = self.config.timeout</td></tr><tr class="hit"><td class="line">78</td><td class="hits">1</td><td class="source"> tc.charset = self.config.charset</td></tr><tr class="hit"><td class="line">79</td><td class="hits">1</td><td class="source"> var tracker = new Tracker(tc)</td></tr><tr class="hit"><td class="line">80</td><td class="hits">1</td><td class="source"> self._trackers.push(tracker)</td></tr><tr class="hit"><td class="line">81</td><td class="hits">1</td><td class="source"> tracker.on('error', function(err) {</td></tr><tr class="miss"><td class="line">82</td><td class="hits">0</td><td class="source"> logger.error(err)</td></tr><tr><td class="line">83</td><td class="hits"></td><td class="source"> // 将有错误的tracker剔除</td></tr><tr class="miss"><td class="line">84</td><td class="hits">0</td><td class="source"> self._trackers.splice(self._trackers.indexOf(tracker), 1)</td></tr><tr><td class="line">85</td><td class="hits"></td><td class="source"> // 检查是否还有可用的tracker</td></tr><tr class="miss"><td class="line">86</td><td class="hits">0</td><td class="source"> if (self._trackers.length === 0) {</td></tr><tr class="miss"><td class="line">87</td><td class="hits">0</td><td class="source"> self.emit('error', new Error('There are no available trackers, please check your tracker config or your tracker server.'));</td></tr><tr><td class="line">88</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">89</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">90</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">91</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">92</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">93</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">94</td><td class="hits">1</td><td class="source">FdfsClient.prototype._errorHandle = function() {</td></tr><tr><td class="line">95</td><td class="hits"></td><td class="source"> // 1. 当没有tracker可用时触发</td></tr><tr><td class="line">96</td><td class="hits"></td><td class="source"> // 2. 当连接storage错误时触发</td></tr><tr class="hit"><td class="line">97</td><td class="hits">1</td><td class="source"> this.on('error', function(err) {</td></tr><tr class="miss"><td class="line">98</td><td class="hits">0</td><td class="source"> logger.error(err)</td></tr><tr><td class="line">99</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">100</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">101</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">102</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">103</td><td class="hits"></td><td class="source"> * 按顺序获取可用的tracker</td></tr><tr><td class="line">104</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">105</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">106</td><td class="hits">1</td><td class="source">FdfsClient.prototype._getTracker = function() {</td></tr><tr class="hit"><td class="line">107</td><td class="hits">27</td><td class="source"> if (null == this._trackerIndex) {</td></tr><tr class="hit"><td class="line">108</td><td class="hits">1</td><td class="source"> this._trackerIndex = 0</td></tr><tr class="hit"><td class="line">109</td><td class="hits">1</td><td class="source"> return this._trackers[0]</td></tr><tr><td class="line">110</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">111</td><td class="hits"></td><td class="source"> } else {</td></tr><tr class="hit"><td class="line">112</td><td class="hits">26</td><td class="source"> this._trackerIndex++</td></tr><tr class="hit"><td class="line">113</td><td class="hits">26</td><td class="source"> if (this._trackerIndex >= this._trackers.length) {</td></tr><tr class="hit"><td class="line">114</td><td class="hits">26</td><td class="source"> this._trackerIndex = 0</td></tr><tr><td class="line">115</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">116</td><td class="hits">26</td><td class="source"> return this._trackers[this._trackerIndex]</td></tr><tr><td class="line">117</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">118</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">119</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">120</td><td class="hits">1</td><td class="source">FdfsClient.prototype._upload = function(file, options, callback) {</td></tr><tr class="hit"><td class="line">121</td><td class="hits">8</td><td class="source"> var tracker = this._getTracker()</td></tr><tr><td class="line">122</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">123</td><td class="hits">8</td><td class="source"> tracker.getStoreStorage(options.group, function(err, storage) {</td></tr><tr class="hit"><td class="line">124</td><td class="hits">8</td><td class="source"> if (null != err) {</td></tr><tr class="miss"><td class="line">125</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">126</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">127</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">128</td><td class="hits">8</td><td class="source"> storage.upload(file, options, callback)</td></tr><tr><td class="line">129</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">130</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">131</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">132</td><td class="hits"></td><td class="source">// ------------- public methods</td></tr><tr><td class="line">133</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">134</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">135</td><td class="hits"></td><td class="source"> * 上传文件</td></tr><tr><td class="line">136</td><td class="hits"></td><td class="source"> * @param file absolute file path or Buffer or ReadableStream</td></tr><tr><td class="line">137</td><td class="hits"></td><td class="source"> * @param options</td></tr><tr><td class="line">138</td><td class="hits"></td><td class="source"> * options.group: 指定要上传的group, 不指定则由tracker server分配</td></tr><tr><td class="line">139</td><td class="hits"></td><td class="source"> * options.size: file size, file参数为ReadableStream时必须指定</td></tr><tr><td class="line">140</td><td class="hits"></td><td class="source"> * options.ext: 上传文件的后缀,不指定则获取file参数的后缀,不含(.)</td></tr><tr><td class="line">141</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">142</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">143</td><td class="hits">1</td><td class="source">FdfsClient.prototype.upload = function(file, options, callback) {</td></tr><tr class="hit"><td class="line">144</td><td class="hits">8</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">145</td><td class="hits">8</td><td class="source"> if (util.isFunction(options)) {</td></tr><tr class="hit"><td class="line">146</td><td class="hits">6</td><td class="source"> callback = options</td></tr><tr class="hit"><td class="line">147</td><td class="hits">6</td><td class="source"> options = {}</td></tr><tr><td class="line">148</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">149</td><td class="hits"></td><td class="source"> } else {</td></tr><tr class="hit"><td class="line">150</td><td class="hits">2</td><td class="source"> if (!options) {</td></tr><tr class="miss"><td class="line">151</td><td class="hits">0</td><td class="source"> options = {}</td></tr><tr><td class="line">152</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">153</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">154</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">155</td><td class="hits">8</td><td class="source"> _normalizeUploadParams(file, options, function(err) {</td></tr><tr class="hit"><td class="line">156</td><td class="hits">8</td><td class="source"> if (err) {</td></tr><tr class="miss"><td class="line">157</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">158</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">159</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">160</td><td class="hits">8</td><td class="source"> if (!options.ext) {</td></tr><tr class="miss"><td class="line">161</td><td class="hits">0</td><td class="source"> options.ext = self.defaultExt</td></tr><tr><td class="line">162</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">163</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">164</td><td class="hits">8</td><td class="source"> self._upload(file, options, callback)</td></tr><tr><td class="line">165</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">166</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">167</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">168</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">169</td><td class="hits"></td><td class="source"> * 下载文件</td></tr><tr><td class="line">170</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">171</td><td class="hits"></td><td class="source"> * @param options options可以直接传options.target</td></tr><tr><td class="line">172</td><td class="hits"></td><td class="source"> * options.target 下载的文件流将被写入到这里,可以是本地文件名,也可以是WritableStream,如果为空则每次服务器返回数据的时候都会回调callback</td></tr><tr><td class="line">173</td><td class="hits"></td><td class="source"> * options.offset和options.bytes: 当只想下载文件中的某1片段时指定</td></tr><tr><td class="line">174</td><td class="hits"></td><td class="source"> * @param callback 若未指定options.target,服务器每次数据的返回都会回调,若指定了options.target,则只在结束时回调一次</td></tr><tr><td class="line">175</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">176</td><td class="hits">1</td><td class="source">FdfsClient.prototype.download = function(fileId, options, callback) {</td></tr><tr class="hit"><td class="line">177</td><td class="hits">3</td><td class="source"> if (!options || util.isFunction(options)) {</td></tr><tr class="miss"><td class="line">178</td><td class="hits">0</td><td class="source"> callback(new Error('options.target is not specified'))</td></tr><tr class="miss"><td class="line">179</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">180</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">181</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">182</td><td class="hits"></td><td class="source"> // 直接传入target</td></tr><tr class="hit"><td class="line">183</td><td class="hits">3</td><td class="source"> if (!options.target) {</td></tr><tr class="hit"><td class="line">184</td><td class="hits">2</td><td class="source"> var ori = options</td></tr><tr class="hit"><td class="line">185</td><td class="hits">2</td><td class="source"> options = {}</td></tr><tr class="hit"><td class="line">186</td><td class="hits">2</td><td class="source"> options.target = ori</td></tr><tr><td class="line">187</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">188</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">189</td><td class="hits">3</td><td class="source"> if (!(util.isString(options.target) || is.writableStream(options.target))) {</td></tr><tr class="miss"><td class="line">190</td><td class="hits">0</td><td class="source"> callback(new Error('options.target is invalid, it\'s type must be String or WritableStream'))</td></tr><tr><td class="line">191</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">192</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">193</td><td class="hits">3</td><td class="source"> if (util.isString(options.target)) {</td></tr><tr class="hit"><td class="line">194</td><td class="hits">3</td><td class="source"> options.target = fs.createWriteStream(options.target)</td></tr><tr><td class="line">195</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">196</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">197</td><td class="hits">3</td><td class="source"> this._getTracker().getFetchStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">198</td><td class="hits">3</td><td class="source"> storage.download(fileId, options, callback)</td></tr><tr><td class="line">199</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">200</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">201</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">202</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">203</td><td class="hits"></td><td class="source"> * 删除fileId指定的文件</td></tr><tr><td class="line">204</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">205</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">206</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">207</td><td class="hits">1</td><td class="source">FdfsClient.prototype.del = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">208</td><td class="hits">7</td><td class="source"> this._getTracker().getUpdateStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">209</td><td class="hits">7</td><td class="source"> if (null != err) {</td></tr><tr class="miss"><td class="line">210</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">211</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">212</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">213</td><td class="hits">7</td><td class="source"> storage.del(fileId, callback)</td></tr><tr><td class="line">214</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">215</td><td class="hits"></td><td class="source">}</td></tr><tr class="hit"><td class="line">216</td><td class="hits">1</td><td class="source">FdfsClient.prototype.remove = FdfsClient.prototype.del</td></tr><tr><td class="line">217</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">218</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">219</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">220</td><td class="hits"></td><td class="source"> * @param metaData {key1: value1, key2: value2}</td></tr><tr><td class="line">221</td><td class="hits"></td><td class="source"> * @param flag 'O' for overwrite all old metadata (default)</td></tr><tr><td class="line">222</td><td class="hits"></td><td class="source"> 'M' for merge, insert when the meta item not exist, otherwise update it</td></tr><tr><td class="line">223</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">224</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">225</td><td class="hits">1</td><td class="source">FdfsClient.prototype.setMetaData = function(fileId, metaData, flag, callback) {</td></tr><tr class="hit"><td class="line">226</td><td class="hits">5</td><td class="source"> if (util.isFunction(flag)) {</td></tr><tr class="hit"><td class="line">227</td><td class="hits">3</td><td class="source"> callback = flag</td></tr><tr class="hit"><td class="line">228</td><td class="hits">3</td><td class="source"> flag = 'O'</td></tr><tr><td class="line">229</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">230</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">231</td><td class="hits">5</td><td class="source"> this._getTracker().getUpdateStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">232</td><td class="hits">5</td><td class="source"> if (null != err) {</td></tr><tr class="miss"><td class="line">233</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">234</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">235</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">236</td><td class="hits">5</td><td class="source"> storage.setMetaData(fileId, metaData, flag, callback)</td></tr><tr><td class="line">237</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">238</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">239</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">240</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">241</td><td class="hits"></td><td class="source"> * 获取指定fileId的meta data</td></tr><tr><td class="line">242</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">243</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">244</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">245</td><td class="hits">1</td><td class="source">FdfsClient.prototype.getMetaData = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">246</td><td class="hits">3</td><td class="source"> this._getTracker().getUpdateStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">247</td><td class="hits">3</td><td class="source"> if (null != err) {</td></tr><tr class="miss"><td class="line">248</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">249</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">250</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">251</td><td class="hits">3</td><td class="source"> storage.getMetaData(fileId, callback)</td></tr><tr><td class="line">252</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">253</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">254</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">255</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">256</td><td class="hits"></td><td class="source"> * 获取指定fileId的信息</td></tr><tr><td class="line">257</td><td class="hits"></td><td class="source"> * fileInfo会传给回调,结构如下</td></tr><tr><td class="line">258</td><td class="hits"></td><td class="source"> * {</td></tr><tr><td class="line">259</td><td class="hits"></td><td class="source"> * // 文件大小</td></tr><tr><td class="line">260</td><td class="hits"></td><td class="source"> * size:</td></tr><tr><td class="line">261</td><td class="hits"></td><td class="source"> * // 文件创建的UTC时间戳,单位为秒</td></tr><tr><td class="line">262</td><td class="hits"></td><td class="source"> * timestamp:</td></tr><tr><td class="line">263</td><td class="hits"></td><td class="source"> * crc32:</td></tr><tr><td class="line">264</td><td class="hits"></td><td class="source"> * // 最初上传到的storage server的ip</td></tr><tr><td class="line">265</td><td class="hits"></td><td class="source"> * addr:</td></tr><tr><td class="line">266</td><td class="hits"></td><td class="source"> * }</td></tr><tr><td class="line">267</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">268</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">269</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">270</td><td class="hits">1</td><td class="source">FdfsClient.prototype.getFileInfo = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">271</td><td class="hits">1</td><td class="source"> this._getTracker().getUpdateStorage(fileId, function(err, storage) {</td></tr><tr class="hit"><td class="line">272</td><td class="hits">1</td><td class="source"> if (err) {</td></tr><tr class="miss"><td class="line">273</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">274</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">275</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">276</td><td class="hits">1</td><td class="source"> storage.getFileInfo(fileId, callback)</td></tr><tr><td class="line">277</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">278</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">279</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">280</td><td class="hits"></td><td class="source">// -------------- helpers</td></tr><tr><td class="line">281</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">282</td><td class="hits"></td><td class="source"> * 验证file参数是否合法,同时补充一些必要的参数</td></tr><tr><td class="line">283</td><td class="hits"></td><td class="source"> * 若为String,则需验证是否存在</td></tr><tr><td class="line">284</td><td class="hits"></td><td class="source"> * 若为ReadableStream,则需验证options.size是否存在</td></tr><tr><td class="line">285</td><td class="hits"></td><td class="source"> * @param file</td></tr><tr><td class="line">286</td><td class="hits"></td><td class="source"> * @param options</td></tr><tr><td class="line">287</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">288</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">289</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">290</td><td class="hits">1</td><td class="source">function _normalizeUploadParams(file, options, callback) {</td></tr><tr class="hit"><td class="line">291</td><td class="hits">8</td><td class="source"> if (!file) {</td></tr><tr class="miss"><td class="line">292</td><td class="hits">0</td><td class="source"> callback(new Error('The "file" parameter is empty.'))</td></tr><tr class="miss"><td class="line">293</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">294</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">295</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">296</td><td class="hits">8</td><td class="source"> if (!(util.isString(file) || util.isBuffer(file) || is.readableStream(file))) {</td></tr><tr class="miss"><td class="line">297</td><td class="hits">0</td><td class="source"> callback(new Error('The "file" parameter is invalid, it must be a String, Buffer, or ReadableStream'))</td></tr><tr class="miss"><td class="line">298</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">299</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">300</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">301</td><td class="hits">8</td><td class="source"> if (util.isString(file)) {</td></tr><tr class="hit"><td class="line">302</td><td class="hits">8</td><td class="source"> fs.stat(file, function(err, stats) {</td></tr><tr class="hit"><td class="line">303</td><td class="hits">8</td><td class="source"> if (err || !stats) {</td></tr><tr class="miss"><td class="line">304</td><td class="hits">0</td><td class="source"> callback(new Error('File [' + file + '] is not exists!'))</td></tr><tr class="miss"><td class="line">305</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">306</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">307</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">308</td><td class="hits">8</td><td class="source"> options.size = stats.size</td></tr><tr class="hit"><td class="line">309</td><td class="hits">8</td><td class="source"> if (!options.ext) {</td></tr><tr class="hit"><td class="line">310</td><td class="hits">7</td><td class="source"> options.ext = path.extname(file)</td></tr><tr class="hit"><td class="line">311</td><td class="hits">7</td><td class="source"> if (options.ext) {</td></tr><tr><td class="line">312</td><td class="hits"></td><td class="source"> // 去掉.</td></tr><tr class="hit"><td class="line">313</td><td class="hits">7</td><td class="source"> options.ext = options.ext.substring(1)</td></tr><tr><td class="line">314</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">315</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">316</td><td class="hits">8</td><td class="source"> callback(null)</td></tr><tr><td class="line">317</td><td class="hits"></td><td class="source"> })</td></tr><tr class="hit"><td class="line">318</td><td class="hits">8</td><td class="source"> return</td></tr><tr><td class="line">319</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">320</td><td class="hits"></td><td class="source"> </td></tr><tr class="miss"><td class="line">321</td><td class="hits">0</td><td class="source"> if (is.readableStream(file) && !options.size) {</td></tr><tr class="miss"><td class="line">322</td><td class="hits">0</td><td class="source"> callback(new Error('when the "file" parameter\'s is ReadableStream, options.size must specified'))</td></tr><tr class="miss"><td class="line">323</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">324</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">325</td><td class="hits"></td><td class="source"> </td></tr><tr class="miss"><td class="line">326</td><td class="hits">0</td><td class="source"> if (util.isBuffer(file)) {</td></tr><tr class="miss"><td class="line">327</td><td class="hits">0</td><td class="source"> options.size = file.length</td></tr><tr><td class="line">328</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">329</td><td class="hits"></td><td class="source"> </td></tr><tr class="miss"><td class="line">330</td><td class="hits">0</td><td class="source"> callback(null)</td></tr><tr><td class="line">331</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">332</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">333</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">334</td><td class="hits"></td><td class="source">// expose</td></tr><tr class="hit"><td class="line">335</td><td class="hits">1</td><td class="source">module.exports = FdfsClient</td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/helpers.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/helpers.js</h2><div id="stats" class="high"><div class="percentage">93%</div><div class="sloc">30</div><div class="hits">28</div><div class="misses">2</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-13</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午8:50</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict'</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var BigNumber = require('bignumber.js')</td></tr><tr class="hit"><td class="line">9</td><td class="hits">1</td><td class="source">var Buffer = require('buffer').Buffer</td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var trimReg = /^\u0000+|\u0000+$/g</td></tr><tr><td class="line">11</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">module.exports = {</td></tr><tr><td class="line">13</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">14</td><td class="hits"></td><td class="source"> * 将unsigned number转换为Big-Endian buffer</td></tr><tr><td class="line">15</td><td class="hits"></td><td class="source"> * @param number</td></tr><tr><td class="line">16</td><td class="hits"></td><td class="source"> * @param bytes buffer的字节数</td></tr><tr><td class="line">17</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">18</td><td class="hits"></td><td class="source"> number2Buffer: function(number, bytes) {</td></tr><tr class="hit"><td class="line">19</td><td class="hits">130</td><td class="source"> if (number < 0) {</td></tr><tr class="miss"><td class="line">20</td><td class="hits">0</td><td class="source"> throw new Error('"number" must greater than or equal to zero.')</td></tr><tr><td class="line">21</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">22</td><td class="hits">130</td><td class="source"> bytes = bytes || 8</td></tr><tr class="hit"><td class="line">23</td><td class="hits">130</td><td class="source"> var bn = new BigNumber(String(number))</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source"> // 转换为16进制字符串</td></tr><tr class="hit"><td class="line">25</td><td class="hits">130</td><td class="source"> var hex = bn.toString(16)</td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source"> // ------- 将hex length补充成bytes x 2,不足则高位补0,超过则去掉高位</td></tr><tr class="hit"><td class="line">28</td><td class="hits">130</td><td class="source"> var length = hex.length</td></tr><tr class="hit"><td class="line">29</td><td class="hits">130</td><td class="source"> var targetLength = bytes * 2</td></tr><tr class="hit"><td class="line">30</td><td class="hits">130</td><td class="source"> if (length < targetLength) {</td></tr><tr class="hit"><td class="line">31</td><td class="hits">129</td><td class="source"> var i = targetLength - length</td></tr><tr class="hit"><td class="line">32</td><td class="hits">129</td><td class="source"> while (i > 0) {</td></tr><tr class="hit"><td class="line">33</td><td class="hits">1849</td><td class="source"> hex = '0' + hex</td></tr><tr class="hit"><td class="line">34</td><td class="hits">1849</td><td class="source"> i--</td></tr><tr><td class="line">35</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">36</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">37</td><td class="hits">1</td><td class="source"> } else if (length > targetLength) {</td></tr><tr class="miss"><td class="line">38</td><td class="hits">0</td><td class="source"> hex = hex.substring(length - targetLength)</td></tr><tr><td class="line">39</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">40</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">41</td><td class="hits"></td><td class="source"> // ------ 填充到buffer里,高位在前</td></tr><tr class="hit"><td class="line">42</td><td class="hits">130</td><td class="source"> var buffer = new Buffer(bytes)</td></tr><tr class="hit"><td class="line">43</td><td class="hits">130</td><td class="source"> var offset = 0</td></tr><tr class="hit"><td class="line">44</td><td class="hits">130</td><td class="source"> while (offset < bytes) {</td></tr><tr class="hit"><td class="line">45</td><td class="hits">1036</td><td class="source"> var bn = Number("0x" + hex.substring(offset * 2, (offset * 2) + 2))</td></tr><tr class="hit"><td class="line">46</td><td class="hits">1036</td><td class="source"> buffer.writeUInt8(bn, offset)</td></tr><tr class="hit"><td class="line">47</td><td class="hits">1036</td><td class="source"> offset++</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">49</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">50</td><td class="hits">130</td><td class="source"> return buffer</td></tr><tr><td class="line">51</td><td class="hits"></td><td class="source"> },</td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">53</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">54</td><td class="hits"></td><td class="source"> * 构造Object.defineProperty中指定的property属性,并达到const声明的效果,只读,不可写</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source"> * @param value</td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source"> * @returns {{configurable: boolean, writable: boolean, value: *}}</td></tr><tr><td class="line">57</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">58</td><td class="hits"></td><td class="source"> buildConstProp: function(value) {</td></tr><tr class="hit"><td class="line">59</td><td class="hits">64</td><td class="source"> return {</td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source"> configurable: false,</td></tr><tr><td class="line">61</td><td class="hits"></td><td class="source"> writable: false,</td></tr><tr><td class="line">62</td><td class="hits"></td><td class="source"> value: value</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">64</td><td class="hits"></td><td class="source"> },</td></tr><tr><td class="line">65</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">66</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">67</td><td class="hits"></td><td class="source"> * 在String.prototype.trim的基础上再去掉\u0000</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source"> * 默认trim的处理见下:</td></tr><tr><td class="line">69</td><td class="hits"></td><td class="source"> * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/trim</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source"> * http://blog.stevenlevithan.com/archives/faster-trim-javascript</td></tr><tr><td class="line">71</td><td class="hits"></td><td class="source"> * http://jsperf.com/mega-trim-test</td></tr><tr><td class="line">72</td><td class="hits"></td><td class="source"> * @param str</td></tr><tr><td class="line">73</td><td class="hits"></td><td class="source"> * @returns {string}</td></tr><tr><td class="line">74</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">75</td><td class="hits"></td><td class="source"> trim: function(str) {</td></tr><tr class="hit"><td class="line">76</td><td class="hits">88</td><td class="source"> return str.trim().replace(trimReg, '')</td></tr><tr><td class="line">77</td><td class="hits"></td><td class="source"> },</td></tr><tr><td class="line">78</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">79</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">80</td><td class="hits"></td><td class="source"> * file id conver to group and filename</td></tr><tr><td class="line">81</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">82</td><td class="hits"></td><td class="source"> * @returns {{group: string, filename: string}}</td></tr><tr><td class="line">83</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">84</td><td class="hits"></td><td class="source"> id2gf: function(fileId) {</td></tr><tr class="hit"><td class="line">85</td><td class="hits">39</td><td class="source"> var pos = fileId.indexOf('/')</td></tr><tr class="hit"><td class="line">86</td><td class="hits">39</td><td class="source"> return {</td></tr><tr><td class="line">87</td><td class="hits"></td><td class="source"> group: fileId.substring(0, pos),</td></tr><tr><td class="line">88</td><td class="hits"></td><td class="source"> filename: fileId.substring(pos + 1)</td></tr><tr><td class="line">89</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">90</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">91</td><td class="hits"></td><td class="source">}</td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/logger.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/logger.js</h2><div id="stats" class="high"><div class="percentage">100%</div><div class="sloc">10</div><div class="hits">10</div><div class="misses">0</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-15</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午1:59</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict';</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var _logger = console</td></tr><tr><td class="line">9</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var logger = {</td></tr><tr><td class="line">11</td><td class="hits"></td><td class="source"> setLogger: function(newLogger) {</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source"> _logger = newLogger</td></tr><tr><td class="line">13</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">14</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">15</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">16</td><td class="hits">1</td><td class="source">var methods = 'log trace debug info warn error fatal'.split(' ')</td></tr><tr class="hit"><td class="line">17</td><td class="hits">1</td><td class="source">methods.forEach(function(method) {</td></tr><tr class="hit"><td class="line">18</td><td class="hits">7</td><td class="source"> logger[method] = function() {</td></tr><tr class="hit"><td class="line">19</td><td class="hits">251</td><td class="source"> var oriLog = _logger[method] || _logger['log']</td></tr><tr class="hit"><td class="line">20</td><td class="hits">251</td><td class="source"> oriLog.apply(_logger, arguments)</td></tr><tr><td class="line">21</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">22</td><td class="hits"></td><td class="source">})</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">24</td><td class="hits">1</td><td class="source">module.exports = logger</td></tr><tr><td class="line">25</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source"> </td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/protocol.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/protocol.js</h2><div id="stats" class="high"><div class="percentage">87%</div><div class="sloc">110</div><div class="hits">96</div><div class="misses">14</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * 协议相关的处理</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * 协议参照 http://bbs.chinaunix.net/thread-2001015-1-1.html</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> * Date: 14-6-15</td></tr><tr><td class="line">6</td><td class="hits"></td><td class="source"> * Time: 上午9:36</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">'use strict'</td></tr><tr><td class="line">9</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var helpers = require('./helpers')</td></tr><tr class="hit"><td class="line">11</td><td class="hits">1</td><td class="source">var Buffer = require('buffer').Buffer</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">var _ = require('lodash')</td></tr><tr class="hit"><td class="line">13</td><td class="hits">1</td><td class="source">var logger = require('./logger')</td></tr><tr class="hit"><td class="line">14</td><td class="hits">1</td><td class="source">var util = require('util')</td></tr><tr class="hit"><td class="line">15</td><td class="hits">1</td><td class="source">var BigNumber = require('bignumber.js')</td></tr><tr><td class="line">16</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">17</td><td class="hits">1</td><td class="source">var protocol = {}</td></tr><tr><td class="line">18</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">19</td><td class="hits">1</td><td class="source">Object.defineProperties(protocol, {</td></tr><tr><td class="line">20</td><td class="hits"></td><td class="source"> FDFS_PROTO_CMD_QUIT: helpers.buildConstProp(82),</td></tr><tr><td class="line">21</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVER_LIST_GROUP: helpers.buildConstProp(91),</td></tr><tr><td class="line">22</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVER_LIST_STORAGE: helpers.buildConstProp(92),</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE: helpers.buildConstProp(93),</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">25</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE: helpers.buildConstProp(101),</td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE: helpers.buildConstProp(102),</td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE: helpers.buildConstProp(103),</td></tr><tr><td class="line">28</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE: helpers.buildConstProp(104),</td></tr><tr><td class="line">29</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL: helpers.buildConstProp(105),</td></tr><tr><td class="line">30</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL: helpers.buildConstProp(106),</td></tr><tr><td class="line">31</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL: helpers.buildConstProp(107),</td></tr><tr><td class="line">32</td><td class="hits"></td><td class="source"> TRACKER_PROTO_CMD_RESP: helpers.buildConstProp(100),</td></tr><tr><td class="line">33</td><td class="hits"></td><td class="source"> FDFS_PROTO_CMD_ACTIVE_TEST: helpers.buildConstProp(111),</td></tr><tr><td class="line">34</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_UPLOAD_FILE: helpers.buildConstProp(11),</td></tr><tr><td class="line">35</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_DELETE_FILE: helpers.buildConstProp(12),</td></tr><tr><td class="line">36</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_SET_METADATA: helpers.buildConstProp(13),</td></tr><tr><td class="line">37</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_DOWNLOAD_FILE: helpers.buildConstProp(14),</td></tr><tr><td class="line">38</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_GET_METADATA: helpers.buildConstProp(15),</td></tr><tr><td class="line">39</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE: helpers.buildConstProp(21),</td></tr><tr><td class="line">40</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_QUERY_FILE_INFO: helpers.buildConstProp(22),</td></tr><tr><td class="line">41</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE: helpers.buildConstProp(23), //create appender file</td></tr><tr><td class="line">42</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_APPEND_FILE: helpers.buildConstProp(24), //append file</td></tr><tr><td class="line">43</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_MODIFY_FILE: helpers.buildConstProp(34), //modify appender file</td></tr><tr><td class="line">44</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_TRUNCATE_FILE: helpers.buildConstProp(36), //truncate appender file</td></tr><tr><td class="line">45</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">46</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">47</td><td class="hits"></td><td class="source"> FDFS_STORAGE_STATUS_INIT: helpers.buildConstProp(0),</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source"> FDFS_STORAGE_STATUS_WAIT_SYNC: helpers.buildConstProp(1),</td></tr><tr><td class="line">49</td><td class="hits"></td><td class="source"> FDFS_STORAGE_STATUS_SYNCING: helpers.buildConstProp(2),</td></tr><tr><td class="line">50</td><td class="hits"></td><td class="source"> FDFS_STORAGE_STATUS_IP_CHANGED: helpers.buildConstProp(3),</td></tr><tr><td class="line">51</td><td class="hits"></td><td class="source"> FDFS_STORAGE_STATUS_DELETED: helpers.buildConstProp(4),</td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source"> FDFS_STORAGE_STATUS_OFFLINE: helpers.buildConstProp(5),</td></tr><tr><td class="line">53</td><td class="hits"></td><td class="source"> FDFS_STORAGE_STATUS_ONLINE: helpers.buildConstProp(6),</td></tr><tr><td class="line">54</td><td class="hits"></td><td class="source"> FDFS_STORAGE_STATUS_ACTIVE: helpers.buildConstProp(7),</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source"> FDFS_STORAGE_STATUS_NONE: helpers.buildConstProp(99),</td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">57</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">58</td><td class="hits"></td><td class="source"> * for overwrite all old metadata</td></tr><tr><td class="line">59</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source"> STORAGE_SET_METADATA_FLAG_OVERWRITE: helpers.buildConstProp('O'),</td></tr><tr><td class="line">61</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">62</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source"> * for replace, insert when the meta item not exist, otherwise update it</td></tr><tr><td class="line">64</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">65</td><td class="hits"></td><td class="source"> STORAGE_SET_METADATA_FLAG_MERGE: helpers.buildConstProp('M'),</td></tr><tr><td class="line">66</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">67</td><td class="hits"></td><td class="source"> FDFS_PROTO_PKG_LEN_SIZE: helpers.buildConstProp(8),</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source"> FDFS_PROTO_CMD_SIZE: helpers.buildConstProp(1),</td></tr><tr><td class="line">69</td><td class="hits"></td><td class="source"> FDFS_GROUP_NAME_MAX_LEN: helpers.buildConstProp(16),</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source"> FDFS_IPADDR_SIZE: helpers.buildConstProp(16),</td></tr><tr><td class="line">71</td><td class="hits"></td><td class="source"> FDFS_DOMAIN_NAME_MAX_SIZE: helpers.buildConstProp(128),</td></tr><tr><td class="line">72</td><td class="hits"></td><td class="source"> FDFS_VERSION_SIZE: helpers.buildConstProp(6),</td></tr><tr><td class="line">73</td><td class="hits"></td><td class="source"> FDFS_STORAGE_ID_MAX_SIZE: helpers.buildConstProp(16),</td></tr><tr><td class="line">74</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">75</td><td class="hits"></td><td class="source"> FDFS_RECORD_SEPERATOR: helpers.buildConstProp('\u0001'),</td></tr><tr><td class="line">76</td><td class="hits"></td><td class="source"> FDFS_FIELD_SEPERATOR: helpers.buildConstProp('\u0002'),</td></tr><tr><td class="line">77</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">78</td><td class="hits"></td><td class="source"> FDFS_FILE_EXT_NAME_MAX_LEN: helpers.buildConstProp(6),</td></tr><tr><td class="line">79</td><td class="hits"></td><td class="source"> FDFS_FILE_PREFIX_MAX_LEN: helpers.buildConstProp(16),</td></tr><tr><td class="line">80</td><td class="hits"></td><td class="source"> FDFS_FILE_PATH_LEN: helpers.buildConstProp(10),</td></tr><tr><td class="line">81</td><td class="hits"></td><td class="source"> FDFS_FILENAME_BASE64_LENGTH: helpers.buildConstProp(27),</td></tr><tr><td class="line">82</td><td class="hits"></td><td class="source"> FDFS_TRUNK_FILE_INFO_LEN: helpers.buildConstProp(16),</td></tr><tr><td class="line">83</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">84</td><td class="hits"></td><td class="source"> ERR_NO_ENOENT: helpers.buildConstProp(2),</td></tr><tr><td class="line">85</td><td class="hits"></td><td class="source"> ERR_NO_EIO: helpers.buildConstProp(5),</td></tr><tr><td class="line">86</td><td class="hits"></td><td class="source"> ERR_NO_EBUSY: helpers.buildConstProp(16),</td></tr><tr><td class="line">87</td><td class="hits"></td><td class="source"> ERR_NO_EINVAL: helpers.buildConstProp(22),</td></tr><tr><td class="line">88</td><td class="hits"></td><td class="source"> ERR_NO_ENOSPC: helpers.buildConstProp(28),</td></tr><tr><td class="line">89</td><td class="hits"></td><td class="source"> ECONNREFUSED: helpers.buildConstProp(61),</td></tr><tr><td class="line">90</td><td class="hits"></td><td class="source"> ERR_NO_EALREADY: helpers.buildConstProp(114),</td></tr><tr><td class="line">91</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">92</td><td class="hits"></td><td class="source"> // 成功的STATUS</td></tr><tr><td class="line">93</td><td class="hits"></td><td class="source"> HEADER_STATUS_SUCCESS: helpers.buildConstProp(0)</td></tr><tr><td class="line">94</td><td class="hits"></td><td class="source">})</td></tr><tr><td class="line">95</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">96</td><td class="hits">1</td><td class="source">Object.defineProperties(protocol, {</td></tr><tr><td class="line">97</td><td class="hits"></td><td class="source"> STORAGE_PROTO_CMD_RESP: helpers.buildConstProp(protocol.TRACKER_PROTO_CMD_RESP),</td></tr><tr><td class="line">98</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">99</td><td class="hits"></td><td class="source"> TRACKER_QUERY_STORAGE_FETCH_BODY_LEN: helpers.buildConstProp(protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE),</td></tr><tr><td class="line">100</td><td class="hits"></td><td class="source"> TRACKER_QUERY_STORAGE_STORE_BODY_LEN: helpers.buildConstProp(protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE),</td></tr><tr><td class="line">101</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">102</td><td class="hits"></td><td class="source"> PROTO_HEADER_CMD_INDEX: helpers.buildConstProp(protocol.FDFS_PROTO_PKG_LEN_SIZE),</td></tr><tr><td class="line">103</td><td class="hits"></td><td class="source"> PROTO_HEADER_STATUS_INDEX: helpers.buildConstProp(protocol.FDFS_PROTO_PKG_LEN_SIZE + 1),</td></tr><tr><td class="line">104</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">105</td><td class="hits"></td><td class="source"> HEADER_BYTE_LENGTH: helpers.buildConstProp(protocol.FDFS_PROTO_PKG_LEN_SIZE + 2)</td></tr><tr><td class="line">106</td><td class="hits"></td><td class="source">})</td></tr><tr><td class="line">107</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">108</td><td class="hits"></td><td class="source">// 协议相关的封装方法</td></tr><tr class="hit"><td class="line">109</td><td class="hits">1</td><td class="source">_.extend(protocol, {</td></tr><tr><td class="line">110</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">111</td><td class="hits"></td><td class="source"> * 封装协议头</td></tr><tr><td class="line">112</td><td class="hits"></td><td class="source"> * @param command</td></tr><tr><td class="line">113</td><td class="hits"></td><td class="source"> * @param bodyLength</td></tr><tr><td class="line">114</td><td class="hits"></td><td class="source"> * @param status</td></tr><tr><td class="line">115</td><td class="hits"></td><td class="source"> * @return {Buffer}</td></tr><tr><td class="line">116</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">117</td><td class="hits"></td><td class="source"> packHeader: function(command, bodyLength, status) {</td></tr><tr class="hit"><td class="line">118</td><td class="hits">109</td><td class="source"> if (null == bodyLength) {</td></tr><tr class="miss"><td class="line">119</td><td class="hits">0</td><td class="source"> bodyLength = 0</td></tr><tr><td class="line">120</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">121</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">122</td><td class="hits">109</td><td class="source"> if (null == status) {</td></tr><tr class="miss"><td class="line">123</td><td class="hits">0</td><td class="source"> status = 0</td></tr><tr><td class="line">124</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">125</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">126</td><td class="hits"></td><td class="source"> // ----------- 存放1字节的command和1字节的status</td></tr><tr class="hit"><td class="line">127</td><td class="hits">109</td><td class="source"> var buffer = new Buffer(2)</td></tr><tr class="hit"><td class="line">128</td><td class="hits">109</td><td class="source"> buffer.writeUInt8(command, 0)</td></tr><tr class="hit"><td class="line">129</td><td class="hits">109</td><td class="source"> buffer.writeUInt8(status, 1)</td></tr><tr><td class="line">130</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">131</td><td class="hits"></td><td class="source"> // 生成8bytes存放body length的buffer</td></tr><tr class="hit"><td class="line">132</td><td class="hits">109</td><td class="source"> var blBuffer = helpers.number2Buffer(bodyLength, 8)</td></tr><tr><td class="line">133</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">134</td><td class="hits"></td><td class="source"> // 拼接</td></tr><tr class="hit"><td class="line">135</td><td class="hits">109</td><td class="source"> return Buffer.concat([blBuffer, buffer])</td></tr><tr><td class="line">136</td><td class="hits"></td><td class="source"> },</td></tr><tr><td class="line">137</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">138</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">139</td><td class="hits"></td><td class="source"> * 解析返回的包</td></tr><tr><td class="line">140</td><td class="hits"></td><td class="source"> * @param socket</td></tr><tr><td class="line">141</td><td class="hits"></td><td class="source"> * @param expectedCommand</td></tr><tr><td class="line">142</td><td class="hits"></td><td class="source"> * @param expectedBodyLength</td></tr><tr><td class="line">143</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">144</td><td class="hits"></td><td class="source"> * @param headerOnly 是否指parse header,下载文件时,解析完header后需将data传递给callback,而非将body接收完</td></tr><tr><td class="line">145</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">146</td><td class="hits"></td><td class="source"> recvPacket: function(socket, expectedCommand, expectedBodyLength, callback, headerOnly) {</td></tr><tr class="hit"><td class="line">147</td><td class="hits">54</td><td class="source"> var oriCallback = callback</td></tr><tr><td class="line">148</td><td class="hits"></td><td class="source"> // 收包完毕则关闭掉连接</td></tr><tr class="hit"><td class="line">149</td><td class="hits">54</td><td class="source"> callback = function() {</td></tr><tr class="hit"><td class="line">150</td><td class="hits">52</td><td class="source"> cleanup()</td></tr><tr class="hit"><td class="line">151</td><td class="hits">52</td><td class="source"> if (util.isFunction(callback)) {</td></tr><tr class="hit"><td class="line">152</td><td class="hits">52</td><td class="source"> oriCallback.apply(null, arguments)</td></tr><tr><td class="line">153</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">154</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">155</td><td class="hits">54</td><td class="source"> var headerBufferLen = protocol.HEADER_BYTE_LENGTH</td></tr><tr class="hit"><td class="line">156</td><td class="hits">54</td><td class="source"> var headerBuffer = new Buffer(headerBufferLen)</td></tr><tr><td class="line">157</td><td class="hits"></td><td class="source"> // 已填充的length</td></tr><tr class="hit"><td class="line">158</td><td class="hits">54</td><td class="source"> var headerBufferFilled = 0</td></tr><tr><td class="line">159</td><td class="hits"></td><td class="source"> // 解析后的header信息</td></tr><tr><td class="line">160</td><td class="hits"></td><td class="source"> // {status: , bodyLength: }</td></tr><tr class="hit"><td class="line">161</td><td class="hits">54</td><td class="source"> var header</td></tr><tr class="hit"><td class="line">162</td><td class="hits">54</td><td class="source"> var bodyBuffer</td></tr><tr><td class="line">163</td><td class="hits"></td><td class="source"> // 下1次要copy到bodyBuffer中的起始位置</td></tr><tr class="hit"><td class="line">164</td><td class="hits">54</td><td class="source"> var bodyBufferStart = 0</td></tr><tr><td class="line">165</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">166</td><td class="hits">54</td><td class="source"> socket.on('data', listener)</td></tr><tr><td class="line">167</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">168</td><td class="hits">54</td><td class="source"> function listener(data) {</td></tr><tr><td class="line">169</td><td class="hits"></td><td class="source"> // --------------- 收到服务器发来消息</td></tr><tr><td class="line">170</td><td class="hits"></td><td class="source"> // -------- 解析header</td></tr><tr class="hit"><td class="line">171</td><td class="hits">54</td><td class="source"> if (!header) {</td></tr><tr><td class="line">172</td><td class="hits"></td><td class="source"> // header parsed</td></tr><tr class="hit"><td class="line">173</td><td class="hits">54</td><td class="source"> if (headerBufferFilled + data.length >= headerBufferLen) {</td></tr><tr class="hit"><td class="line">174</td><td class="hits">54</td><td class="source"> var len = headerBufferFilled + data.length</td></tr><tr><td class="line">175</td><td class="hits"></td><td class="source"> // 只copy剩下的header部分</td></tr><tr class="hit"><td class="line">176</td><td class="hits">54</td><td class="source"> data.copy(headerBuffer, headerBufferFilled, 0, headerBufferLen - headerBufferFilled)</td></tr><tr class="hit"><td class="line">177</td><td class="hits">54</td><td class="source"> try {</td></tr><tr class="hit"><td class="line">178</td><td class="hits">54</td><td class="source"> header = _parseHeader(headerBuffer, expectedCommand, expectedBodyLength)</td></tr><tr class="hit"><td class="line">179</td><td class="hits">53</td><td class="source"> logger.debug('receive server packet header: %j', header)</td></tr><tr><td class="line">180</td><td class="hits"></td><td class="source"> // 无body时直接返回</td></tr><tr class="hit"><td class="line">181</td><td class="hits">53</td><td class="source"> if (header.bodyLength === 0) {</td></tr><tr class="hit"><td class="line">182</td><td class="hits">12</td><td class="source"> callback(null, header)</td></tr><tr class="hit"><td class="line">183</td><td class="hits">12</td><td class="source"> return</td></tr><tr><td class="line">184</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">185</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">186</td><td class="hits">41</td><td class="source"> if (headerOnly) {</td></tr><tr class="hit"><td class="line">187</td><td class="hits">2</td><td class="source"> oriCallback(null, header)</td></tr><tr class="hit"><td class="line">188</td><td class="hits">2</td><td class="source"> if (len > headerBufferLen) {</td></tr><tr class="hit"><td class="line">189</td><td class="hits">2</td><td class="source"> oriCallback(null, data.slice(headerBufferLen - headerBufferFilled))</td></tr><tr><td class="line">190</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">191</td><td class="hits">2</td><td class="source"> return</td></tr><tr><td class="line">192</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">193</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">194</td><td class="hits">39</td><td class="source"> bodyBuffer = new Buffer(header.bodyLength)</td></tr><tr><td class="line">195</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">196</td><td class="hits"></td><td class="source"> // 还有body的数据则填充到body buffer中</td></tr><tr class="hit"><td class="line">197</td><td class="hits">39</td><td class="source"> if (len > headerBufferLen) {</td></tr><tr class="hit"><td class="line">198</td><td class="hits">39</td><td class="source"> data.copy(bodyBuffer, 0, headerBufferLen - headerBufferFilled)</td></tr><tr class="hit"><td class="line">199</td><td class="hits">39</td><td class="source"> bodyBufferStart = len - headerBufferLen</td></tr><tr><td class="line">200</td><td class="hits"></td><td class="source"> // 读取完毕</td></tr><tr class="hit"><td class="line">201</td><td class="hits">39</td><td class="source"> if (bodyBufferStart >= header.bodyLength) {</td></tr><tr class="hit"><td class="line">202</td><td class="hits">39</td><td class="source"> callback(null, bodyBuffer)</td></tr><tr><td class="line">203</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">204</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">205</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">206</td><td class="hits"></td><td class="source"> } catch (err) {</td></tr><tr class="hit"><td class="line">207</td><td class="hits">1</td><td class="source"> callback(err)</td></tr><tr><td class="line">208</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">209</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">210</td><td class="hits"></td><td class="source"> } else {</td></tr><tr class="miss"><td class="line">211</td><td class="hits">0</td><td class="source"> data.copy(headerBuffer, headerBufferFilled)</td></tr><tr class="miss"><td class="line">212</td><td class="hits">0</td><td class="source"> headerBufferFilled += data.length</td></tr><tr><td class="line">213</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">214</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">215</td><td class="hits"></td><td class="source"> } else {</td></tr><tr><td class="line">216</td><td class="hits"></td><td class="source"> // 交由外部处理</td></tr><tr class="miss"><td class="line">217</td><td class="hits">0</td><td class="source"> if (headerOnly) {</td></tr><tr class="miss"><td class="line">218</td><td class="hits">0</td><td class="source"> oriCallback(null, data)</td></tr><tr class="miss"><td class="line">219</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">220</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">221</td><td class="hits"></td><td class="source"> // ---------- 解析body</td></tr><tr class="miss"><td class="line">222</td><td class="hits">0</td><td class="source"> data.copy(bodyBuffer, bodyBufferStart)</td></tr><tr class="miss"><td class="line">223</td><td class="hits">0</td><td class="source"> bodyBufferStart += data.length</td></tr><tr><td class="line">224</td><td class="hits"></td><td class="source"> // 读取完毕</td></tr><tr class="miss"><td class="line">225</td><td class="hits">0</td><td class="source"> if (bodyBufferStart >= header.bodyLength) {</td></tr><tr class="miss"><td class="line">226</td><td class="hits">0</td><td class="source"> callback(null, bodyBuffer)</td></tr><tr><td class="line">227</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">228</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">229</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">230</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">231</td><td class="hits">54</td><td class="source"> function cleanup() {</td></tr><tr class="hit"><td class="line">232</td><td class="hits">52</td><td class="source"> socket.removeListener('data', listener)</td></tr><tr><td class="line">233</td><td class="hits"></td><td class="source"> // 关闭连接</td></tr><tr class="hit"><td class="line">234</td><td class="hits">52</td><td class="source"> protocol.closeSocket(socket)</td></tr><tr><td class="line">235</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">236</td><td class="hits"></td><td class="source"> },</td></tr><tr><td class="line">237</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">238</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">239</td><td class="hits"></td><td class="source"> * 给服务器发送关闭指令,同时end socket</td></tr><tr><td class="line">240</td><td class="hits"></td><td class="source"> * @param socket</td></tr><tr><td class="line">241</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">242</td><td class="hits"></td><td class="source"> closeSocket: function(socket) {</td></tr><tr class="hit"><td class="line">243</td><td class="hits">54</td><td class="source"> socket.end(protocol.packHeader(protocol.FDFS_PROTO_CMD_QUIT, 0, 0))</td></tr><tr><td class="line">244</td><td class="hits"></td><td class="source"> },</td></tr><tr><td class="line">245</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">246</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">247</td><td class="hits"></td><td class="source"> * 封装只有fileId的包,下载和删除文件时使用</td></tr><tr><td class="line">248</td><td class="hits"></td><td class="source"> * @param command</td></tr><tr><td class="line">249</td><td class="hits"></td><td class="source"> * @param group</td></tr><tr><td class="line">250</td><td class="hits"></td><td class="source"> * @param filename</td></tr><tr><td class="line">251</td><td class="hits"></td><td class="source"> * @param charset</td></tr><tr><td class="line">252</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">253</td><td class="hits"></td><td class="source"> packFileId: function(command, group, filename, charset) {</td></tr><tr><td class="line">254</td><td class="hits"></td><td class="source"> // --------- 封装header</td></tr><tr class="hit"><td class="line">255</td><td class="hits">30</td><td class="source"> var fnLength = Buffer.byteLength(filename, charset)</td></tr><tr class="hit"><td class="line">256</td><td class="hits">30</td><td class="source"> var bodyLength = protocol.FDFS_GROUP_NAME_MAX_LEN + fnLength</td></tr><tr class="hit"><td class="line">257</td><td class="hits">30</td><td class="source"> var header = protocol.packHeader(command, bodyLength, 0)</td></tr><tr><td class="line">258</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">259</td><td class="hits"></td><td class="source"> // --------- 封装body</td></tr><tr class="hit"><td class="line">260</td><td class="hits">30</td><td class="source"> var body = new Buffer(bodyLength)</td></tr><tr><td class="line">261</td><td class="hits"></td><td class="source"> // 默认都填充上0</td></tr><tr class="hit"><td class="line">262</td><td class="hits">30</td><td class="source"> body.fill(0)</td></tr><tr class="hit"><td class="line">263</td><td class="hits">30</td><td class="source"> var groupBL = Buffer.byteLength(group, charset)</td></tr><tr class="hit"><td class="line">264</td><td class="hits">30</td><td class="source"> body.write(group, 0, groupBL, charset)</td></tr><tr class="hit"><td class="line">265</td><td class="hits">30</td><td class="source"> body.write(filename, protocol.FDFS_GROUP_NAME_MAX_LEN, fnLength, charset)</td></tr><tr><td class="line">266</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">267</td><td class="hits">30</td><td class="source"> return Buffer.concat([header, body])</td></tr><tr><td class="line">268</td><td class="hits"></td><td class="source"> },</td></tr><tr><td class="line">269</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">270</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">271</td><td class="hits"></td><td class="source"> * 封装meta data</td></tr><tr><td class="line">272</td><td class="hits"></td><td class="source"> * 注:返回的是string,由外部写入到buffer中</td></tr><tr><td class="line">273</td><td class="hits"></td><td class="source"> * @param {Object} metaData</td></tr><tr><td class="line">274</td><td class="hits"></td><td class="source"> * @return {String}</td></tr><tr><td class="line">275</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">276</td><td class="hits"></td><td class="source"> packMetaData: function(metaData) {</td></tr><tr class="hit"><td class="line">277</td><td class="hits">6</td><td class="source"> var first = true</td></tr><tr class="hit"><td class="line">278</td><td class="hits">6</td><td class="source"> var result = ''</td></tr><tr><td class="line">279</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">280</td><td class="hits">6</td><td class="source"> Object.keys(metaData).forEach(function(key) {</td></tr><tr class="hit"><td class="line">281</td><td class="hits">10</td><td class="source"> if (!first) {</td></tr><tr class="hit"><td class="line">282</td><td class="hits">4</td><td class="source"> result += protocol.FDFS_RECORD_SEPERATOR</td></tr><tr><td class="line">283</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">284</td><td class="hits"></td><td class="source"> } else {</td></tr><tr class="hit"><td class="line">285</td><td class="hits">6</td><td class="source"> first = false</td></tr><tr><td class="line">286</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">287</td><td class="hits">10</td><td class="source"> var value = metaData[key]</td></tr><tr class="hit"><td class="line">288</td><td class="hits">10</td><td class="source"> result += key</td></tr><tr class="hit"><td class="line">289</td><td class="hits">10</td><td class="source"> result += protocol.FDFS_FIELD_SEPERATOR</td></tr><tr class="hit"><td class="line">290</td><td class="hits">10</td><td class="source"> result += value</td></tr><tr><td class="line">291</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">292</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">293</td><td class="hits">6</td><td class="source"> return result</td></tr><tr><td class="line">294</td><td class="hits"></td><td class="source"> },</td></tr><tr><td class="line">295</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">296</td><td class="hits"></td><td class="source"> /**</td></tr><tr><td class="line">297</td><td class="hits"></td><td class="source"> * raw meta data to structure</td></tr><tr><td class="line">298</td><td class="hits"></td><td class="source"> * @param raw</td></tr><tr><td class="line">299</td><td class="hits"></td><td class="source"> */</td></tr><tr><td class="line">300</td><td class="hits"></td><td class="source"> parseMetaData: function(raw) {</td></tr><tr class="hit"><td class="line">301</td><td class="hits">4</td><td class="source"> var result = {}</td></tr><tr class="hit"><td class="line">302</td><td class="hits">4</td><td class="source"> var md = raw.split(protocol.FDFS_RECORD_SEPERATOR)</td></tr><tr class="hit"><td class="line">303</td><td class="hits">4</td><td class="source"> md.forEach(function(item) {</td></tr><tr class="hit"><td class="line">304</td><td class="hits">8</td><td class="source"> var arr = item.split(protocol.FDFS_FIELD_SEPERATOR)</td></tr><tr class="hit"><td class="line">305</td><td class="hits">8</td><td class="source"> var key = helpers.trim(arr[0])</td></tr><tr class="hit"><td class="line">306</td><td class="hits">8</td><td class="source"> var value = helpers.trim(arr[1])</td></tr><tr class="hit"><td class="line">307</td><td class="hits">8</td><td class="source"> result[key] = value</td></tr><tr><td class="line">308</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">309</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">310</td><td class="hits">4</td><td class="source"> return result</td></tr><tr><td class="line">311</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">312</td><td class="hits"></td><td class="source">})</td></tr><tr><td class="line">313</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">314</td><td class="hits">1</td><td class="source">function _parseHeader(headerBuffer, expectedCommand, expectedBodyLength) {</td></tr><tr><td class="line">315</td><td class="hits"></td><td class="source"> // validate buffer length</td></tr><tr class="hit"><td class="line">316</td><td class="hits">54</td><td class="source"> if (headerBuffer.length !== protocol.FDFS_PROTO_PKG_LEN_SIZE + 2) {</td></tr><tr class="miss"><td class="line">317</td><td class="hits">0</td><td class="source"> throw new Error('receive packet size ' + headerBuffer.length + ' is not equal to the expected header size: ' + protocol.FDFS_PROTO_PKG_LEN_SIZE + 2)</td></tr><tr><td class="line">318</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">319</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">320</td><td class="hits"></td><td class="source"> // validate command</td></tr><tr class="hit"><td class="line">321</td><td class="hits">54</td><td class="source"> var command = Number('0x' + headerBuffer.toString('hex', protocol.PROTO_HEADER_CMD_INDEX, protocol.PROTO_HEADER_CMD_INDEX + 1))</td></tr><tr class="hit"><td class="line">322</td><td class="hits">54</td><td class="source"> if (expectedCommand !== command) {</td></tr><tr class="miss"><td class="line">323</td><td class="hits">0</td><td class="source"> throw new Error('receive command: ' + command + ' is not equal to the expected command: ' + expectedCommand)</td></tr><tr><td class="line">324</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">325</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">326</td><td class="hits"></td><td class="source"> // 应用层错误</td></tr><tr class="hit"><td class="line">327</td><td class="hits">54</td><td class="source"> var status = Number('0x' + headerBuffer.toString('hex', protocol.PROTO_HEADER_STATUS_INDEX, protocol.PROTO_HEADER_STATUS_INDEX + 1))</td></tr><tr class="hit"><td class="line">328</td><td class="hits">54</td><td class="source"> if (status !== protocol.HEADER_STATUS_SUCCESS) {</td></tr><tr class="hit"><td class="line">329</td><td class="hits">1</td><td class="source"> throw new Error('receive packet errno is: ' + status)</td></tr><tr><td class="line">330</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">331</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">332</td><td class="hits"></td><td class="source"> // validate body length</td></tr><tr class="hit"><td class="line">333</td><td class="hits">53</td><td class="source"> var bodyLength = new BigNumber(headerBuffer.toString('hex', 0, protocol.FDFS_PROTO_PKG_LEN_SIZE), 16).toNumber()</td></tr><tr class="hit"><td class="line">334</td><td class="hits">53</td><td class="source"> if (null != expectedBodyLength && expectedBodyLength !== bodyLength) {</td></tr><tr class="miss"><td class="line">335</td><td class="hits">0</td><td class="source"> throw new Error('receive packet body length: ' + bodyLength + ' is not equal to the expected: ' + expectedBodyLength)</td></tr><tr><td class="line">336</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">337</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">338</td><td class="hits">53</td><td class="source"> return {</td></tr><tr><td class="line">339</td><td class="hits"></td><td class="source"> status: protocol.HEADER_STATUS_SUCCESS,</td></tr><tr><td class="line">340</td><td class="hits"></td><td class="source"> bodyLength: bodyLength</td></tr><tr><td class="line">341</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">342</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">343</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">344</td><td class="hits">1</td><td class="source">module.exports = protocol</td></tr><tr><td class="line">345</td><td class="hits"></td><td class="source"> </td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/storage.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/storage.js</h2><div id="stats" class="high"><div class="percentage">91%</div><div class="sloc">167</div><div class="hits">153</div><div class="misses">14</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-13</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午10:18</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict';</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var EventEmitter = require('events').EventEmitter</td></tr><tr class="hit"><td class="line">9</td><td class="hits">1</td><td class="source">var util = require('util')</td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var net = require('net')</td></tr><tr class="hit"><td class="line">11</td><td class="hits">1</td><td class="source">var logger = require('./logger')</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">var protocol = require('./protocol')</td></tr><tr class="hit"><td class="line">13</td><td class="hits">1</td><td class="source">var fs = require('fs')</td></tr><tr class="hit"><td class="line">14</td><td class="hits">1</td><td class="source">var helpers = require('./helpers')</td></tr><tr class="hit"><td class="line">15</td><td class="hits">1</td><td class="source">var BigNumber = require('bignumber.js')</td></tr><tr><td class="line">16</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">17</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">18</td><td class="hits">1</td><td class="source">function Storage(config) {</td></tr><tr class="hit"><td class="line">19</td><td class="hits">27</td><td class="source"> EventEmitter.call(this)</td></tr><tr><td class="line">20</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">21</td><td class="hits">27</td><td class="source"> this.config = config</td></tr><tr class="hit"><td class="line">22</td><td class="hits">27</td><td class="source"> this._name = config.host + ':' + config.port</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">25</td><td class="hits">1</td><td class="source">util.inherits(Storage, EventEmitter)</td></tr><tr><td class="line">26</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source">// ------------- private methods</td></tr><tr><td class="line">28</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">29</td><td class="hits">1</td><td class="source">Storage.prototype._getConnection = function() {</td></tr><tr class="hit"><td class="line">30</td><td class="hits">27</td><td class="source"> return this._newConnection()</td></tr><tr><td class="line">31</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">32</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">33</td><td class="hits">1</td><td class="source">Storage.prototype._newConnection = function() {</td></tr><tr class="hit"><td class="line">34</td><td class="hits">27</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">35</td><td class="hits">27</td><td class="source"> var socket = new net.Socket()</td></tr><tr class="hit"><td class="line">36</td><td class="hits">27</td><td class="source"> logger.debug('connect to storage server [%s].', this._name)</td></tr><tr class="hit"><td class="line">37</td><td class="hits">27</td><td class="source"> socket.setTimeout(this.config.timeout)</td></tr><tr class="hit"><td class="line">38</td><td class="hits">27</td><td class="source"> socket.connect(this.config.port, this.config.host)</td></tr><tr><td class="line">39</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">40</td><td class="hits">27</td><td class="source"> socket.on('error', function(err) {</td></tr><tr class="miss"><td class="line">41</td><td class="hits">0</td><td class="source"> self.emit('error', err)</td></tr><tr><td class="line">42</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">43</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">44</td><td class="hits">27</td><td class="source"> socket.on('timeout', function() {</td></tr><tr class="miss"><td class="line">45</td><td class="hits">0</td><td class="source"> socket.destroy()</td></tr><tr class="miss"><td class="line">46</td><td class="hits">0</td><td class="source"> self.emit('error', new Error('connect to storage server [' + self._name + '] timeout.'))</td></tr><tr><td class="line">47</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">49</td><td class="hits">27</td><td class="source"> socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">50</td><td class="hits">27</td><td class="source"> logger.debug('storage server [%s] is connected', self._name)</td></tr><tr><td class="line">51</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">53</td><td class="hits">27</td><td class="source"> return socket</td></tr><tr><td class="line">54</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source">// --------- upload相关</td></tr><tr><td class="line">57</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">58</td><td class="hits"></td><td class="source">// ------------- public methods</td></tr><tr><td class="line">59</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">61</td><td class="hits"></td><td class="source"> * # request body:</td></tr><tr><td class="line">62</td><td class="hits"></td><td class="source"> @ 1 byte: store path index on the storage server</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: file size</td></tr><tr><td class="line">64</td><td class="hits"></td><td class="source"> @ FDFS_FILE_EXT_NAME_MAX_LEN bytes: file ext name, do not include dot (.)</td></tr><tr><td class="line">65</td><td class="hits"></td><td class="source"> @ file size bytes: file content</td></tr><tr><td class="line">66</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">67</td><td class="hits"></td><td class="source"> # response body:</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">69</td><td class="hits"></td><td class="source"> @ filename bytes: filename</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source"> * @param file</td></tr><tr><td class="line">71</td><td class="hits"></td><td class="source"> * @param options</td></tr><tr><td class="line">72</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">73</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">74</td><td class="hits">1</td><td class="source">Storage.prototype.upload = function(file, options, callback) {</td></tr><tr class="hit"><td class="line">75</td><td class="hits">8</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">76</td><td class="hits">8</td><td class="source"> var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">77</td><td class="hits">8</td><td class="source"> socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">78</td><td class="hits">8</td><td class="source"> logger.debug('start upload file to storage server [%s]', self._name)</td></tr><tr><td class="line">79</td><td class="hits"></td><td class="source"> // ------------- 封装header并发送</td></tr><tr class="hit"><td class="line">80</td><td class="hits">8</td><td class="source"> var command = protocol.STORAGE_PROTO_CMD_UPLOAD_FILE</td></tr><tr class="hit"><td class="line">81</td><td class="hits">8</td><td class="source"> var bodyLength = 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_FILE_EXT_NAME_MAX_LEN + options.size</td></tr><tr class="hit"><td class="line">82</td><td class="hits">8</td><td class="source"> var header = protocol.packHeader(command, bodyLength, 0)</td></tr><tr class="hit"><td class="line">83</td><td class="hits">8</td><td class="source"> socket.write(header)</td></tr><tr><td class="line">84</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">85</td><td class="hits"></td><td class="source"> // ------------- 封装并发送body</td></tr><tr><td class="line">86</td><td class="hits"></td><td class="source"> // ------ 除file content外的内容</td></tr><tr class="hit"><td class="line">87</td><td class="hits">8</td><td class="source"> var buffer = new Buffer(1 + protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_FILE_EXT_NAME_MAX_LEN)</td></tr><tr class="hit"><td class="line">88</td><td class="hits">8</td><td class="source"> buffer.fill(0)</td></tr><tr class="hit"><td class="line">89</td><td class="hits">8</td><td class="source"> buffer.writeUInt8(self.config.storePathIndex, 0)</td></tr><tr class="hit"><td class="line">90</td><td class="hits">8</td><td class="source"> helpers.number2Buffer(options.size, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(buffer, 1)</td></tr><tr class="hit"><td class="line">91</td><td class="hits">8</td><td class="source"> var extBL = Buffer.byteLength(options.ext, self.config.charset)</td></tr><tr class="hit"><td class="line">92</td><td class="hits">8</td><td class="source"> buffer.write(options.ext, 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE, extBL, self.config.charset)</td></tr><tr><td class="line">93</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">94</td><td class="hits">8</td><td class="source"> socket.write(buffer)</td></tr><tr><td class="line">95</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">96</td><td class="hits"></td><td class="source"> // ------ 发送file content</td></tr><tr class="hit"><td class="line">97</td><td class="hits">8</td><td class="source"> if (util.isString(file)) {</td></tr><tr class="hit"><td class="line">98</td><td class="hits">8</td><td class="source"> file = fs.createReadStream(file)</td></tr><tr><td class="line">99</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">100</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">101</td><td class="hits"></td><td class="source"> // buffer</td></tr><tr class="hit"><td class="line">102</td><td class="hits">8</td><td class="source"> if (util.isBuffer(file)) {</td></tr><tr class="miss"><td class="line">103</td><td class="hits">0</td><td class="source"> socket.write(file)</td></tr><tr><td class="line">104</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">105</td><td class="hits"></td><td class="source"> // stream</td></tr><tr><td class="line">106</td><td class="hits"></td><td class="source"> } else {</td></tr><tr class="hit"><td class="line">107</td><td class="hits">8</td><td class="source"> file.pipe(socket, {end: false})</td></tr><tr><td class="line">108</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">109</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">110</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">111</td><td class="hits">8</td><td class="source"> protocol.recvPacket(</td></tr><tr><td class="line">112</td><td class="hits"></td><td class="source"> socket,</td></tr><tr><td class="line">113</td><td class="hits"></td><td class="source"> protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">114</td><td class="hits"></td><td class="source"> null,</td></tr><tr><td class="line">115</td><td class="hits"></td><td class="source"> function(err, body) {</td></tr><tr class="hit"><td class="line">116</td><td class="hits">8</td><td class="source"> if (null != err) {</td></tr><tr class="miss"><td class="line">117</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">118</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">119</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">120</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">121</td><td class="hits"></td><td class="source"> // 校验body</td></tr><tr class="hit"><td class="line">122</td><td class="hits">8</td><td class="source"> if (body.length <= protocol.FDFS_GROUP_NAME_MAX_LEN) {</td></tr><tr class="miss"><td class="line">123</td><td class="hits">0</td><td class="source"> callback(new Error('response body length: ' + body.length + ' <= ' + protocol.FDFS_GROUP_NAME_MAX_LEN))</td></tr><tr class="miss"><td class="line">124</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">125</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">126</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">127</td><td class="hits">8</td><td class="source"> var fileId = _parseFileId(body, self.config.charset)</td></tr><tr class="hit"><td class="line">128</td><td class="hits">8</td><td class="source"> callback(null, fileId)</td></tr><tr><td class="line">129</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">130</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">131</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">132</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">133</td><td class="hits"></td><td class="source"> * * STORAGE_PROTO_CMD_SET_METADATA</td></tr><tr><td class="line">134</td><td class="hits"></td><td class="source"> *</td></tr><tr><td class="line">135</td><td class="hits"></td><td class="source"> # function: set meta data</td></tr><tr><td class="line">136</td><td class="hits"></td><td class="source"> # request body:</td></tr><tr><td class="line">137</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: filename length</td></tr><tr><td class="line">138</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: meta data size</td></tr><tr><td class="line">139</td><td class="hits"></td><td class="source"> @ 1 bytes: operation flag,</td></tr><tr><td class="line">140</td><td class="hits"></td><td class="source"> 'O' for overwrite all old metadata</td></tr><tr><td class="line">141</td><td class="hits"></td><td class="source"> 'M' for merge, insert when the meta item not exist, otherwise update it</td></tr><tr><td class="line">142</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">143</td><td class="hits"></td><td class="source"> @ filename bytes: filename</td></tr><tr><td class="line">144</td><td class="hits"></td><td class="source"> @ meta data bytes: each meta data seperated by \x01,</td></tr><tr><td class="line">145</td><td class="hits"></td><td class="source"> name and value seperated by \x02</td></tr><tr><td class="line">146</td><td class="hits"></td><td class="source"> # response body: none</td></tr><tr><td class="line">147</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">148</td><td class="hits"></td><td class="source"> * @param metaData</td></tr><tr><td class="line">149</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">150</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">151</td><td class="hits">1</td><td class="source">Storage.prototype.setMetaData = function(fileId, metaData, flag, callback) {</td></tr><tr class="hit"><td class="line">152</td><td class="hits">5</td><td class="source"> if (!flag) flag = protocol.STORAGE_SET_METADATA_FLAG_OVERWRITE</td></tr><tr><td class="line">153</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">154</td><td class="hits">5</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">155</td><td class="hits">5</td><td class="source"> var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">156</td><td class="hits">5</td><td class="source"> var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">157</td><td class="hits">5</td><td class="source"> var packedMeta = protocol.packMetaData(metaData)</td></tr><tr><td class="line">158</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">159</td><td class="hits">5</td><td class="source"> socket.on('connect', function() {</td></tr><tr><td class="line">160</td><td class="hits"></td><td class="source"> // ------------- 封装header</td></tr><tr class="hit"><td class="line">161</td><td class="hits">5</td><td class="source"> var charset = self.config.charset</td></tr><tr class="hit"><td class="line">162</td><td class="hits">5</td><td class="source"> var command = protocol.STORAGE_PROTO_CMD_SET_METADATA</td></tr><tr class="hit"><td class="line">163</td><td class="hits">5</td><td class="source"> var fnLength = Buffer.byteLength(gf.filename, charset)</td></tr><tr class="hit"><td class="line">164</td><td class="hits">5</td><td class="source"> var metaLength = Buffer.byteLength(packedMeta, charset)</td></tr><tr class="hit"><td class="line">165</td><td class="hits">5</td><td class="source"> var bodyLength = protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + 1 +</td></tr><tr><td class="line">166</td><td class="hits"></td><td class="source"> protocol.FDFS_GROUP_NAME_MAX_LEN + fnLength + metaLength</td></tr><tr><td class="line">167</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">168</td><td class="hits">5</td><td class="source"> var header = protocol.packHeader(command, bodyLength, 0)</td></tr><tr class="hit"><td class="line">169</td><td class="hits">5</td><td class="source"> socket.write(header)</td></tr><tr><td class="line">170</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">171</td><td class="hits"></td><td class="source"> // ------------- 封装body</td></tr><tr class="hit"><td class="line">172</td><td class="hits">5</td><td class="source"> var groupLength = Buffer.byteLength(gf.group, charset)</td></tr><tr class="hit"><td class="line">173</td><td class="hits">5</td><td class="source"> var body = new Buffer(bodyLength)</td></tr><tr class="hit"><td class="line">174</td><td class="hits">5</td><td class="source"> body.fill(0)</td></tr><tr class="hit"><td class="line">175</td><td class="hits">5</td><td class="source"> helpers.number2Buffer(fnLength, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(body, 0)</td></tr><tr class="hit"><td class="line">176</td><td class="hits">5</td><td class="source"> helpers.number2Buffer(metaLength, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(body, protocol.FDFS_PROTO_PKG_LEN_SIZE)</td></tr><tr class="hit"><td class="line">177</td><td class="hits">5</td><td class="source"> body.write(flag, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE, 1, charset)</td></tr><tr class="hit"><td class="line">178</td><td class="hits">5</td><td class="source"> body.write(gf.group, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + 1, groupLength, charset)</td></tr><tr class="hit"><td class="line">179</td><td class="hits">5</td><td class="source"> body.write(gf.filename, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + 1 + protocol.FDFS_GROUP_NAME_MAX_LEN, fnLength, charset)</td></tr><tr class="hit"><td class="line">180</td><td class="hits">5</td><td class="source"> body.write(packedMeta, bodyLength - metaLength, metaLength, charset)</td></tr><tr><td class="line">181</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">182</td><td class="hits">5</td><td class="source"> socket.write(body)</td></tr><tr><td class="line">183</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">184</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">185</td><td class="hits">5</td><td class="source"> protocol.recvPacket(</td></tr><tr><td class="line">186</td><td class="hits"></td><td class="source"> socket,</td></tr><tr><td class="line">187</td><td class="hits"></td><td class="source"> protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">188</td><td class="hits"></td><td class="source"> 0,</td></tr><tr><td class="line">189</td><td class="hits"></td><td class="source"> callback)</td></tr><tr><td class="line">190</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">191</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">192</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">193</td><td class="hits"></td><td class="source"> * * STORAGE_PROTO_CMD_GET_METADATA</td></tr><tr><td class="line">194</td><td class="hits"></td><td class="source"> # function: get metat data from storage server</td></tr><tr><td class="line">195</td><td class="hits"></td><td class="source"> # request body:</td></tr><tr><td class="line">196</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">197</td><td class="hits"></td><td class="source"> @ filename bytes: filename</td></tr><tr><td class="line">198</td><td class="hits"></td><td class="source"> # response body</td></tr><tr><td class="line">199</td><td class="hits"></td><td class="source"> @ meta data buff, each meta data seperated by \x01, name and value seperated by \x02</td></tr><tr><td class="line">200</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">201</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">202</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">203</td><td class="hits">1</td><td class="source">Storage.prototype.getMetaData = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">204</td><td class="hits">3</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">205</td><td class="hits">3</td><td class="source"> var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">206</td><td class="hits">3</td><td class="source"> var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">207</td><td class="hits">3</td><td class="source"> socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">208</td><td class="hits">3</td><td class="source"> var packet = protocol.packFileId(protocol.STORAGE_PROTO_CMD_GET_METADATA, gf.group, gf.filename, self.config.charset)</td></tr><tr class="hit"><td class="line">209</td><td class="hits">3</td><td class="source"> socket.write(packet)</td></tr><tr><td class="line">210</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">211</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">212</td><td class="hits">3</td><td class="source"> protocol.recvPacket(</td></tr><tr><td class="line">213</td><td class="hits"></td><td class="source"> socket,</td></tr><tr><td class="line">214</td><td class="hits"></td><td class="source"> protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">215</td><td class="hits"></td><td class="source"> null,</td></tr><tr><td class="line">216</td><td class="hits"></td><td class="source"> function(err, body) {</td></tr><tr class="hit"><td class="line">217</td><td class="hits">3</td><td class="source"> if (err) {</td></tr><tr class="miss"><td class="line">218</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">219</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">220</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">221</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">222</td><td class="hits">3</td><td class="source"> var rawMeta = body.toString(self.config.charset)</td></tr><tr class="hit"><td class="line">223</td><td class="hits">3</td><td class="source"> if (rawMeta) {</td></tr><tr class="hit"><td class="line">224</td><td class="hits">3</td><td class="source"> var metaData = protocol.parseMetaData(rawMeta)</td></tr><tr class="hit"><td class="line">225</td><td class="hits">3</td><td class="source"> callback(null, metaData)</td></tr><tr><td class="line">226</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">227</td><td class="hits"></td><td class="source"> } else {</td></tr><tr class="miss"><td class="line">228</td><td class="hits">0</td><td class="source"> callback(null, rawMeta)</td></tr><tr><td class="line">229</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">230</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">231</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">232</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">233</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">234</td><td class="hits"></td><td class="source"> * 删除文件</td></tr><tr><td class="line">235</td><td class="hits"></td><td class="source"> * STORAGE_PROTO_CMD_DELETE_FILE</td></tr><tr><td class="line">236</td><td class="hits"></td><td class="source"> * # request body:</td></tr><tr><td class="line">237</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">238</td><td class="hits"></td><td class="source"> @ filename bytes: filename</td></tr><tr><td class="line">239</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">240</td><td class="hits"></td><td class="source"> # response body: none</td></tr><tr><td class="line">241</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">242</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">243</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">244</td><td class="hits">1</td><td class="source">Storage.prototype.del = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">245</td><td class="hits">7</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">246</td><td class="hits">7</td><td class="source"> var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">247</td><td class="hits">7</td><td class="source"> var socket = this._getConnection()</td></tr><tr><td class="line">248</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">249</td><td class="hits">7</td><td class="source"> socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">250</td><td class="hits">7</td><td class="source"> var packet = protocol.packFileId(protocol.STORAGE_PROTO_CMD_DELETE_FILE, gf.group, gf.filename, self.config.charset)</td></tr><tr class="hit"><td class="line">251</td><td class="hits">7</td><td class="source"> socket.write(packet)</td></tr><tr><td class="line">252</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">253</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">254</td><td class="hits">7</td><td class="source"> protocol.recvPacket(</td></tr><tr><td class="line">255</td><td class="hits"></td><td class="source"> socket,</td></tr><tr><td class="line">256</td><td class="hits"></td><td class="source"> protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">257</td><td class="hits"></td><td class="source"> 0,</td></tr><tr><td class="line">258</td><td class="hits"></td><td class="source"> callback)</td></tr><tr><td class="line">259</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">260</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">261</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">262</td><td class="hits"></td><td class="source"> * STORAGE_PROTO_CMD_DOWNLOAD_FILE</td></tr><tr><td class="line">263</td><td class="hits"></td><td class="source"> # function: download/fetch file from storage server</td></tr><tr><td class="line">264</td><td class="hits"></td><td class="source"> # request body:</td></tr><tr><td class="line">265</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: file offset</td></tr><tr><td class="line">266</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: download file bytes</td></tr><tr><td class="line">267</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">268</td><td class="hits"></td><td class="source"> @ filename bytes: filename</td></tr><tr><td class="line">269</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">270</td><td class="hits"></td><td class="source"> # response body:</td></tr><tr><td class="line">271</td><td class="hits"></td><td class="source"> @ file content</td></tr><tr><td class="line">272</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">273</td><td class="hits"></td><td class="source"> * @param options</td></tr><tr><td class="line">274</td><td class="hits"></td><td class="source"> * options.target 下载的文件流将被写入到这里,可以是本地文件名,也可以是WritableStream,如果为空则每次服务器返回数据的时候都会回调callback</td></tr><tr><td class="line">275</td><td class="hits"></td><td class="source"> * options.offset和options.bytes: 当只想下载文件中的某1片段时指定</td></tr><tr><td class="line">276</td><td class="hits"></td><td class="source"> * @param callback 若未指定options.target,服务器每次数据的返回都会回调,若指定了options.target,则只在结束时回调一次</td></tr><tr><td class="line">277</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">278</td><td class="hits">1</td><td class="source">Storage.prototype.download = function(fileId, options, callback) {</td></tr><tr class="hit"><td class="line">279</td><td class="hits">3</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">280</td><td class="hits">3</td><td class="source"> var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">281</td><td class="hits">3</td><td class="source"> var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">282</td><td class="hits">3</td><td class="source"> socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">283</td><td class="hits">3</td><td class="source"> var charset = self.config.charset</td></tr><tr><td class="line">284</td><td class="hits"></td><td class="source"> // --------- 封装header</td></tr><tr class="hit"><td class="line">285</td><td class="hits">3</td><td class="source"> var fnLength = Buffer.byteLength(gf.filename, charset)</td></tr><tr class="hit"><td class="line">286</td><td class="hits">3</td><td class="source"> var bodyLength = protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_GROUP_NAME_MAX_LEN + fnLength</td></tr><tr class="hit"><td class="line">287</td><td class="hits">3</td><td class="source"> var header = protocol.packHeader(protocol.STORAGE_PROTO_CMD_DOWNLOAD_FILE, bodyLength, 0)</td></tr><tr><td class="line">288</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">289</td><td class="hits"></td><td class="source"> // --------- 封装body</td></tr><tr class="hit"><td class="line">290</td><td class="hits">3</td><td class="source"> var body = new Buffer(bodyLength)</td></tr><tr><td class="line">291</td><td class="hits"></td><td class="source"> // 默认都填充上0</td></tr><tr class="hit"><td class="line">292</td><td class="hits">3</td><td class="source"> body.fill(0)</td></tr><tr class="hit"><td class="line">293</td><td class="hits">3</td><td class="source"> if (options.offset) {</td></tr><tr class="hit"><td class="line">294</td><td class="hits">1</td><td class="source"> helpers.number2Buffer(options.offset, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(body)</td></tr><tr><td class="line">295</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">296</td><td class="hits">3</td><td class="source"> if (options.bytes) {</td></tr><tr class="hit"><td class="line">297</td><td class="hits">1</td><td class="source"> helpers.number2Buffer(options.bytes, protocol.FDFS_PROTO_PKG_LEN_SIZE).copy(body, protocol.FDFS_PROTO_PKG_LEN_SIZE)</td></tr><tr><td class="line">298</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">299</td><td class="hits">3</td><td class="source"> var groupBL = Buffer.byteLength(gf.group, charset)</td></tr><tr class="hit"><td class="line">300</td><td class="hits">3</td><td class="source"> body.write(gf.group, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE, groupBL, charset)</td></tr><tr class="hit"><td class="line">301</td><td class="hits">3</td><td class="source"> body.write(gf.filename, protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_PROTO_PKG_LEN_SIZE + protocol.FDFS_GROUP_NAME_MAX_LEN, fnLength, charset)</td></tr><tr><td class="line">302</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">303</td><td class="hits">3</td><td class="source"> socket.write(Buffer.concat([header, body]))</td></tr><tr><td class="line">304</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">305</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">306</td><td class="hits">3</td><td class="source"> var header</td></tr><tr class="hit"><td class="line">307</td><td class="hits">3</td><td class="source"> var target = options.target</td></tr><tr><td class="line">308</td><td class="hits"></td><td class="source"> // 已接收的body length</td></tr><tr class="hit"><td class="line">309</td><td class="hits">3</td><td class="source"> var recvLength = 0</td></tr><tr><td class="line">310</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">311</td><td class="hits">3</td><td class="source"> protocol.recvPacket(</td></tr><tr><td class="line">312</td><td class="hits"></td><td class="source"> socket,</td></tr><tr><td class="line">313</td><td class="hits"></td><td class="source"> protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">314</td><td class="hits"></td><td class="source"> null,</td></tr><tr><td class="line">315</td><td class="hits"></td><td class="source"> function(err, data) {</td></tr><tr class="hit"><td class="line">316</td><td class="hits">5</td><td class="source"> if (err) {</td></tr><tr class="hit"><td class="line">317</td><td class="hits">1</td><td class="source"> callback(err)</td></tr><tr class="hit"><td class="line">318</td><td class="hits">1</td><td class="source"> return</td></tr><tr><td class="line">319</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">320</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">321</td><td class="hits"></td><td class="source"> // header</td></tr><tr class="hit"><td class="line">322</td><td class="hits">4</td><td class="source"> if (!util.isBuffer(data)) {</td></tr><tr class="hit"><td class="line">323</td><td class="hits">2</td><td class="source"> header = data</td></tr><tr><td class="line">324</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">325</td><td class="hits"></td><td class="source"> // body</td></tr><tr><td class="line">326</td><td class="hits"></td><td class="source"> } else {</td></tr><tr class="hit"><td class="line">327</td><td class="hits">2</td><td class="source"> if (!target) {</td></tr><tr class="miss"><td class="line">328</td><td class="hits">0</td><td class="source"> callback(null, data, header.bodyLength)</td></tr><tr><td class="line">329</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">330</td><td class="hits"></td><td class="source"> } else {</td></tr><tr class="hit"><td class="line">331</td><td class="hits">2</td><td class="source"> target.write(data)</td></tr><tr><td class="line">332</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">333</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">334</td><td class="hits">2</td><td class="source"> recvLength += data.length</td></tr><tr><td class="line">335</td><td class="hits"></td><td class="source"> // 读取完毕</td></tr><tr class="hit"><td class="line">336</td><td class="hits">2</td><td class="source"> if (recvLength >= header.bodyLength) {</td></tr><tr class="hit"><td class="line">337</td><td class="hits">2</td><td class="source"> protocol.closeSocket(socket)</td></tr><tr class="hit"><td class="line">338</td><td class="hits">2</td><td class="source"> callback(null)</td></tr><tr><td class="line">339</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">340</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">341</td><td class="hits"></td><td class="source"> },</td></tr><tr><td class="line">342</td><td class="hits"></td><td class="source"> true)</td></tr><tr><td class="line">343</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">344</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">345</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">346</td><td class="hits"></td><td class="source"> * * STORAGE_PROTO_CMD_QUERY_FILE_INFO</td></tr><tr><td class="line">347</td><td class="hits"></td><td class="source"> # function: query file info from storage server</td></tr><tr><td class="line">348</td><td class="hits"></td><td class="source"> # request body:</td></tr><tr><td class="line">349</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">350</td><td class="hits"></td><td class="source"> @ filename bytes: filename</td></tr><tr><td class="line">351</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">352</td><td class="hits"></td><td class="source"> # response body:</td></tr><tr><td class="line">353</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: file size</td></tr><tr><td class="line">354</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: file create timestamp</td></tr><tr><td class="line">355</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: file CRC32 signature</td></tr><tr><td class="line">356</td><td class="hits"></td><td class="source"> @ FDFS_IPADDR_SIZE bytes: file source ip addr</td></tr><tr><td class="line">357</td><td class="hits"></td><td class="source"> * @param fileId</td></tr><tr><td class="line">358</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">359</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">360</td><td class="hits">1</td><td class="source">Storage.prototype.getFileInfo = function(fileId, callback) {</td></tr><tr class="hit"><td class="line">361</td><td class="hits">1</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">362</td><td class="hits">1</td><td class="source"> var gf = helpers.id2gf(fileId)</td></tr><tr class="hit"><td class="line">363</td><td class="hits">1</td><td class="source"> var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">364</td><td class="hits">1</td><td class="source"> var charset = this.config.charset</td></tr><tr><td class="line">365</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">366</td><td class="hits">1</td><td class="source"> socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">367</td><td class="hits">1</td><td class="source"> var packet = protocol.packFileId(protocol.STORAGE_PROTO_CMD_QUERY_FILE_INFO, gf.group, gf.filename, charset)</td></tr><tr class="hit"><td class="line">368</td><td class="hits">1</td><td class="source"> socket.write(packet)</td></tr><tr><td class="line">369</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">370</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">371</td><td class="hits">1</td><td class="source"> protocol.recvPacket(</td></tr><tr><td class="line">372</td><td class="hits"></td><td class="source"> socket,</td></tr><tr><td class="line">373</td><td class="hits"></td><td class="source"> protocol.STORAGE_PROTO_CMD_RESP,</td></tr><tr><td class="line">374</td><td class="hits"></td><td class="source"> protocol.FDFS_PROTO_PKG_LEN_SIZE * 3 + protocol.FDFS_IPADDR_SIZE,</td></tr><tr><td class="line">375</td><td class="hits"></td><td class="source"> function(err, body) {</td></tr><tr class="hit"><td class="line">376</td><td class="hits">1</td><td class="source"> if (err) {</td></tr><tr class="miss"><td class="line">377</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">378</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">379</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">380</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">381</td><td class="hits">1</td><td class="source"> var result = {}</td></tr><tr><td class="line">382</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">383</td><td class="hits">1</td><td class="source"> result.size = new BigNumber(body.toString('hex', 0, protocol.FDFS_PROTO_PKG_LEN_SIZE), 16).toString(10)</td></tr><tr class="hit"><td class="line">384</td><td class="hits">1</td><td class="source"> result.timestamp = new BigNumber(body.toString('hex', protocol.FDFS_PROTO_PKG_LEN_SIZE, protocol.FDFS_PROTO_PKG_LEN_SIZE * 2), 16).toNumber()</td></tr><tr class="hit"><td class="line">385</td><td class="hits">1</td><td class="source"> result.crc32 = new BigNumber(body.toString('hex', protocol.FDFS_PROTO_PKG_LEN_SIZE * 2, protocol.FDFS_PROTO_PKG_LEN_SIZE * 3), 16).toNumber()</td></tr><tr class="hit"><td class="line">386</td><td class="hits">1</td><td class="source"> result.addr = helpers.trim(body.toString(charset, protocol.FDFS_PROTO_PKG_LEN_SIZE * 3))</td></tr><tr><td class="line">387</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">388</td><td class="hits">1</td><td class="source"> callback(null, result)</td></tr><tr><td class="line">389</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">390</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">391</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">392</td><td class="hits"></td><td class="source">// -------------- helpers</td></tr><tr><td class="line">393</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">394</td><td class="hits"></td><td class="source"> * parse file id from body</td></tr><tr><td class="line">395</td><td class="hits"></td><td class="source"> * @param body</td></tr><tr><td class="line">396</td><td class="hits"></td><td class="source"> * @param charset</td></tr><tr><td class="line">397</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">398</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">399</td><td class="hits">1</td><td class="source">function _parseFileId(body, charset) {</td></tr><tr class="hit"><td class="line">400</td><td class="hits">8</td><td class="source"> var group = helpers.trim(body.toString(charset, 0, protocol.FDFS_GROUP_NAME_MAX_LEN))</td></tr><tr class="hit"><td class="line">401</td><td class="hits">8</td><td class="source"> var filename = helpers.trim(body.toString(charset, protocol.FDFS_GROUP_NAME_MAX_LEN))</td></tr><tr><td class="line">402</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">403</td><td class="hits">8</td><td class="source"> return group + '/' + filename</td></tr><tr><td class="line">404</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">405</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">406</td><td class="hits">1</td><td class="source">module.exports = Storage</td></tr></tbody></table></div><div class="file"><h2 id="/Users/chenboxiang/WebstormProjects/fdfs-client/lib/tracker.js">/Users/chenboxiang/WebstormProjects/fdfs-client/lib/tracker.js</h2><div id="stats" class="high"><div class="percentage">92%</div><div class="sloc">105</div><div class="hits">97</div><div class="misses">8</div></div><table id="source"><thead><tr><th>Line</th><th>Hits</th><th>Source</th></tr></thead><tbody><tr><td class="line">1</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">2</td><td class="hits"></td><td class="source"> * Author: chenboxiang</td></tr><tr><td class="line">3</td><td class="hits"></td><td class="source"> * Date: 14-6-13</td></tr><tr><td class="line">4</td><td class="hits"></td><td class="source"> * Time: 下午10:17</td></tr><tr><td class="line">5</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">6</td><td class="hits">1</td><td class="source">'use strict'</td></tr><tr><td class="line">7</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">8</td><td class="hits">1</td><td class="source">var Buffer = require('buffer').Buffer</td></tr><tr class="hit"><td class="line">9</td><td class="hits">1</td><td class="source">var net = require('net')</td></tr><tr class="hit"><td class="line">10</td><td class="hits">1</td><td class="source">var EventEmitter = require('events').EventEmitter</td></tr><tr class="hit"><td class="line">11</td><td class="hits">1</td><td class="source">var util = require('util')</td></tr><tr class="hit"><td class="line">12</td><td class="hits">1</td><td class="source">var logger = require('./logger')</td></tr><tr class="hit"><td class="line">13</td><td class="hits">1</td><td class="source">var protocol = require('./protocol')</td></tr><tr class="hit"><td class="line">14</td><td class="hits">1</td><td class="source">var Storage = require('./storage')</td></tr><tr class="hit"><td class="line">15</td><td class="hits">1</td><td class="source">var helpers = require('./helpers')</td></tr><tr><td class="line">16</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">17</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">18</td><td class="hits">1</td><td class="source">function Tracker(config) {</td></tr><tr class="hit"><td class="line">19</td><td class="hits">1</td><td class="source"> EventEmitter.call(this)</td></tr><tr><td class="line">20</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">21</td><td class="hits">1</td><td class="source"> this.config = config</td></tr><tr class="hit"><td class="line">22</td><td class="hits">1</td><td class="source"> this._name = config.host + ':' + config.port</td></tr><tr><td class="line">23</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">24</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">25</td><td class="hits"></td><td class="source">// extends from EventEmitter</td></tr><tr class="hit"><td class="line">26</td><td class="hits">1</td><td class="source">util.inherits(Tracker, EventEmitter)</td></tr><tr><td class="line">27</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">28</td><td class="hits"></td><td class="source">// ---------------- private methods</td></tr><tr><td class="line">29</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">30</td><td class="hits">1</td><td class="source">Tracker.prototype._getConnection = function() {</td></tr><tr class="hit"><td class="line">31</td><td class="hits">27</td><td class="source"> return this._newConnection()</td></tr><tr><td class="line">32</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">33</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">34</td><td class="hits">1</td><td class="source">Tracker.prototype._newConnection = function() {</td></tr><tr class="hit"><td class="line">35</td><td class="hits">27</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">36</td><td class="hits">27</td><td class="source"> var socket = new net.Socket()</td></tr><tr class="hit"><td class="line">37</td><td class="hits">27</td><td class="source"> logger.debug('connect to tracker server [%s]', this._name)</td></tr><tr class="hit"><td class="line">38</td><td class="hits">27</td><td class="source"> socket.setTimeout(this.config.timeout)</td></tr><tr class="hit"><td class="line">39</td><td class="hits">27</td><td class="source"> socket.connect(this.config.port, this.config.host)</td></tr><tr><td class="line">40</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">41</td><td class="hits">27</td><td class="source"> socket.on('error', function(err) {</td></tr><tr class="miss"><td class="line">42</td><td class="hits">0</td><td class="source"> self.emit('error', err)</td></tr><tr><td class="line">43</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">44</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">45</td><td class="hits">27</td><td class="source"> socket.on('timeout', function() {</td></tr><tr class="miss"><td class="line">46</td><td class="hits">0</td><td class="source"> socket.destroy()</td></tr><tr class="miss"><td class="line">47</td><td class="hits">0</td><td class="source"> self.emit('error', new Error('connect to tracker server [' + self._name + '] timeout.'))</td></tr><tr><td class="line">48</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">49</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">50</td><td class="hits">27</td><td class="source"> socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">51</td><td class="hits">27</td><td class="source"> logger.debug('tracker server [%s] is connected', self._name)</td></tr><tr><td class="line">52</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">53</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">54</td><td class="hits">27</td><td class="source"> return socket</td></tr><tr><td class="line">55</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">56</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">57</td><td class="hits"></td><td class="source">// ---------------- public methods</td></tr><tr><td class="line">58</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">59</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">60</td><td class="hits"></td><td class="source"> * query storage server to upload file</td></tr><tr><td class="line">61</td><td class="hits"></td><td class="source"> * 获取指定group的storage实例,如果不指定则tracker server会随机返回1个</td></tr><tr><td class="line">62</td><td class="hits"></td><td class="source"> * @param group</td></tr><tr><td class="line">63</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">64</td><td class="hits">1</td><td class="source">Tracker.prototype.getStoreStorage = function(group, callback) {</td></tr><tr><td class="line">65</td><td class="hits"></td><td class="source"> // 验证group是否过长</td></tr><tr class="hit"><td class="line">66</td><td class="hits">8</td><td class="source"> if (group && group.length > protocol.FDFS_GROUP_NAME_MAX_LEN) {</td></tr><tr class="miss"><td class="line">67</td><td class="hits">0</td><td class="source"> throw new Error('group name [' + group + '] is too long')</td></tr><tr><td class="line">68</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">69</td><td class="hits">8</td><td class="source"> logger.debug('get a upload storage server from tracker server: [%s]', this._name)</td></tr><tr><td class="line">70</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">71</td><td class="hits">8</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">72</td><td class="hits">8</td><td class="source"> var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">73</td><td class="hits">8</td><td class="source"> socket.on('connect', function() {</td></tr><tr><td class="line">74</td><td class="hits"></td><td class="source"> // ----------- 获取1个可用storage server 信息</td></tr><tr><td class="line">75</td><td class="hits"></td><td class="source"> // -------- 封装header并发送</td></tr><tr class="hit"><td class="line">76</td><td class="hits">8</td><td class="source"> var command</td></tr><tr class="hit"><td class="line">77</td><td class="hits">8</td><td class="source"> var bodyLength</td></tr><tr class="hit"><td class="line">78</td><td class="hits">8</td><td class="source"> if (!group) {</td></tr><tr class="hit"><td class="line">79</td><td class="hits">7</td><td class="source"> command = protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE</td></tr><tr class="hit"><td class="line">80</td><td class="hits">7</td><td class="source"> bodyLength = 0</td></tr><tr><td class="line">81</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">82</td><td class="hits"></td><td class="source"> } else {</td></tr><tr class="hit"><td class="line">83</td><td class="hits">1</td><td class="source"> command = protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE</td></tr><tr class="hit"><td class="line">84</td><td class="hits">1</td><td class="source"> bodyLength = protocol.FDFS_GROUP_NAME_MAX_LEN</td></tr><tr><td class="line">85</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">86</td><td class="hits">8</td><td class="source"> var header = protocol.packHeader(command, bodyLength, 0)</td></tr><tr class="hit"><td class="line">87</td><td class="hits">8</td><td class="source"> logger.debug('send header to tracker server [%s]', self._name)</td></tr><tr class="hit"><td class="line">88</td><td class="hits">8</td><td class="source"> socket.write(header)</td></tr><tr><td class="line">89</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">90</td><td class="hits"></td><td class="source"> // -------- 发送body</td></tr><tr class="hit"><td class="line">91</td><td class="hits">8</td><td class="source"> if (group) {</td></tr><tr class="hit"><td class="line">92</td><td class="hits">1</td><td class="source"> var body = new Buffer(bodyLength)</td></tr><tr><td class="line">93</td><td class="hits"></td><td class="source"> // 默认都填充上0</td></tr><tr class="hit"><td class="line">94</td><td class="hits">1</td><td class="source"> body.fill(0)</td></tr><tr class="hit"><td class="line">95</td><td class="hits">1</td><td class="source"> var groupBL = Buffer.byteLength(group, self.config.charset)</td></tr><tr class="hit"><td class="line">96</td><td class="hits">1</td><td class="source"> body.write(group, 0, groupBL, self.config.charset)</td></tr><tr class="hit"><td class="line">97</td><td class="hits">1</td><td class="source"> logger.debug('send body to tracker server [%s]', self._name)</td></tr><tr class="hit"><td class="line">98</td><td class="hits">1</td><td class="source"> socket.write(body)</td></tr><tr><td class="line">99</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">100</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">101</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">102</td><td class="hits">8</td><td class="source"> protocol.recvPacket(</td></tr><tr><td class="line">103</td><td class="hits"></td><td class="source"> socket,</td></tr><tr><td class="line">104</td><td class="hits"></td><td class="source"> protocol.TRACKER_PROTO_CMD_RESP,</td></tr><tr><td class="line">105</td><td class="hits"></td><td class="source"> protocol.TRACKER_QUERY_STORAGE_STORE_BODY_LEN,</td></tr><tr><td class="line">106</td><td class="hits"></td><td class="source"> function(err, body) {</td></tr><tr class="hit"><td class="line">107</td><td class="hits">8</td><td class="source"> if (null != err) {</td></tr><tr class="miss"><td class="line">108</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">109</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">110</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">111</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">112</td><td class="hits">8</td><td class="source"> var storageConfig = _parseStorage(body, self.config.charset, true)</td></tr><tr class="hit"><td class="line">113</td><td class="hits">8</td><td class="source"> storageConfig.timeout = self.config.timeout</td></tr><tr class="hit"><td class="line">114</td><td class="hits">8</td><td class="source"> storageConfig.charset = self.config.charset</td></tr><tr class="hit"><td class="line">115</td><td class="hits">8</td><td class="source"> var storage = new Storage(storageConfig)</td></tr><tr class="hit"><td class="line">116</td><td class="hits">8</td><td class="source"> logger.debug('get store storage server info: %j ', storage.config)</td></tr><tr class="hit"><td class="line">117</td><td class="hits">8</td><td class="source"> callback(null, storage)</td></tr><tr><td class="line">118</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">119</td><td class="hits"></td><td class="source"> )</td></tr><tr><td class="line">120</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">121</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">122</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">123</td><td class="hits"></td><td class="source"> * query which storage server to download the file</td></tr><tr><td class="line">124</td><td class="hits"></td><td class="source"> * 若只传1个参数则认为是fileId</td></tr><tr><td class="line">125</td><td class="hits"></td><td class="source"> *</td></tr><tr><td class="line">126</td><td class="hits"></td><td class="source"> * * TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE</td></tr><tr><td class="line">127</td><td class="hits"></td><td class="source"> # function: query which storage server to download the file</td></tr><tr><td class="line">128</td><td class="hits"></td><td class="source"> # request body:</td></tr><tr><td class="line">129</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">130</td><td class="hits"></td><td class="source"> @ filename bytes: filename</td></tr><tr><td class="line">131</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">132</td><td class="hits"></td><td class="source"> # response body:</td></tr><tr><td class="line">133</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">134</td><td class="hits"></td><td class="source"> @ FDFS_IPADDR_SIZE - 1 bytes: storage server ip address</td></tr><tr><td class="line">135</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: storage server port</td></tr><tr><td class="line">136</td><td class="hits"></td><td class="source"> *</td></tr><tr><td class="line">137</td><td class="hits"></td><td class="source"> * @param group</td></tr><tr><td class="line">138</td><td class="hits"></td><td class="source"> * @param filename</td></tr><tr><td class="line">139</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">140</td><td class="hits">1</td><td class="source">Tracker.prototype.getFetchStorage = function(group, filename, callback) {</td></tr><tr class="hit"><td class="line">141</td><td class="hits">3</td><td class="source"> logger.debug('get a fetch storage server from tracker server: [%s]', this._name)</td></tr><tr class="hit"><td class="line">142</td><td class="hits">3</td><td class="source"> this.getFetchOrUpdateStorage(protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE, group, filename, callback)</td></tr><tr><td class="line">143</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">144</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">145</td><td class="hits">1</td><td class="source">Tracker.prototype.getUpdateStorage = function(group, filename, callback) {</td></tr><tr class="hit"><td class="line">146</td><td class="hits">16</td><td class="source"> logger.debug('get a update storage server from tracker server: [%s]', this._name)</td></tr><tr class="hit"><td class="line">147</td><td class="hits">16</td><td class="source"> this.getFetchOrUpdateStorage(protocol.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE, group, filename, callback)</td></tr><tr><td class="line">148</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">149</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">150</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">151</td><td class="hits"></td><td class="source"> * # request body:</td></tr><tr><td class="line">152</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">153</td><td class="hits"></td><td class="source"> @ filename bytes: filename</td></tr><tr><td class="line">154</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">155</td><td class="hits"></td><td class="source"> # response body:</td></tr><tr><td class="line">156</td><td class="hits"></td><td class="source"> @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">157</td><td class="hits"></td><td class="source"> @ FDFS_IPADDR_SIZE - 1 bytes: storage server ip address</td></tr><tr><td class="line">158</td><td class="hits"></td><td class="source"> @ FDFS_PROTO_PKG_LEN_SIZE bytes: storage server port</td></tr><tr><td class="line">159</td><td class="hits"></td><td class="source"> * @param command TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE or TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE</td></tr><tr><td class="line">160</td><td class="hits"></td><td class="source"> * @param group</td></tr><tr><td class="line">161</td><td class="hits"></td><td class="source"> * @param filename</td></tr><tr><td class="line">162</td><td class="hits"></td><td class="source"> * @param callback</td></tr><tr><td class="line">163</td><td class="hits"></td><td class="source"> * @private</td></tr><tr><td class="line">164</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">165</td><td class="hits">1</td><td class="source">Tracker.prototype.getFetchOrUpdateStorage = function(command, group, filename, callback) {</td></tr><tr class="hit"><td class="line">166</td><td class="hits">19</td><td class="source"> if (util.isFunction(filename)) {</td></tr><tr class="hit"><td class="line">167</td><td class="hits">19</td><td class="source"> callback = filename</td></tr><tr class="hit"><td class="line">168</td><td class="hits">19</td><td class="source"> var gf = helpers.id2gf(group)</td></tr><tr class="hit"><td class="line">169</td><td class="hits">19</td><td class="source"> group = gf.group</td></tr><tr class="hit"><td class="line">170</td><td class="hits">19</td><td class="source"> filename = gf.filename</td></tr><tr><td class="line">171</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">172</td><td class="hits">19</td><td class="source"> logger.debug('group: %s, filename: %s', group, filename)</td></tr><tr class="hit"><td class="line">173</td><td class="hits">19</td><td class="source"> var self = this</td></tr><tr class="hit"><td class="line">174</td><td class="hits">19</td><td class="source"> var socket = this._getConnection()</td></tr><tr class="hit"><td class="line">175</td><td class="hits">19</td><td class="source"> socket.on('connect', function() {</td></tr><tr class="hit"><td class="line">176</td><td class="hits">19</td><td class="source"> var packet = protocol.packFileId(command, group, filename, self.config.charset)</td></tr><tr class="hit"><td class="line">177</td><td class="hits">19</td><td class="source"> socket.write(packet)</td></tr><tr><td class="line">178</td><td class="hits"></td><td class="source"> })</td></tr><tr><td class="line">179</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">180</td><td class="hits">19</td><td class="source"> protocol.recvPacket(</td></tr><tr><td class="line">181</td><td class="hits"></td><td class="source"> socket,</td></tr><tr><td class="line">182</td><td class="hits"></td><td class="source"> protocol.TRACKER_PROTO_CMD_RESP,</td></tr><tr><td class="line">183</td><td class="hits"></td><td class="source"> protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE,</td></tr><tr><td class="line">184</td><td class="hits"></td><td class="source"> function(err, body) {</td></tr><tr class="hit"><td class="line">185</td><td class="hits">19</td><td class="source"> if (null != err) {</td></tr><tr class="miss"><td class="line">186</td><td class="hits">0</td><td class="source"> callback(err)</td></tr><tr class="miss"><td class="line">187</td><td class="hits">0</td><td class="source"> return</td></tr><tr><td class="line">188</td><td class="hits"></td><td class="source"> }</td></tr><tr class="hit"><td class="line">189</td><td class="hits">19</td><td class="source"> var storageConfig = _parseStorage(body, self.config.charset)</td></tr><tr class="hit"><td class="line">190</td><td class="hits">19</td><td class="source"> storageConfig.timeout = self.config.timeout</td></tr><tr class="hit"><td class="line">191</td><td class="hits">19</td><td class="source"> storageConfig.charset = self.config.charset</td></tr><tr class="hit"><td class="line">192</td><td class="hits">19</td><td class="source"> var storage = new Storage(storageConfig)</td></tr><tr class="hit"><td class="line">193</td><td class="hits">19</td><td class="source"> logger.info('get storage server info: %j ', storage.config)</td></tr><tr class="hit"><td class="line">194</td><td class="hits">19</td><td class="source"> callback(null, storage)</td></tr><tr><td class="line">195</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">196</td><td class="hits"></td><td class="source"> )</td></tr><tr><td class="line">197</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">198</td><td class="hits"></td><td class="source"> </td></tr><tr><td class="line">199</td><td class="hits"></td><td class="source">// -------------- helper methods</td></tr><tr><td class="line">200</td><td class="hits"></td><td class="source">/**</td></tr><tr><td class="line">201</td><td class="hits"></td><td class="source"> * @ FDFS_GROUP_NAME_MAX_LEN bytes: group name</td></tr><tr><td class="line">202</td><td class="hits"></td><td class="source"> * @ FDFS_IPADDR_SIZE - 1 bytes: storage server ip address</td></tr><tr><td class="line">203</td><td class="hits"></td><td class="source"> * @ FDFS_PROTO_PKG_LEN_SIZE bytes: storage server port</td></tr><tr><td class="line">204</td><td class="hits"></td><td class="source"> * @1 byte: store path index on the storage server {可以没有这个字节}</td></tr><tr><td class="line">205</td><td class="hits"></td><td class="source"> *</td></tr><tr><td class="line">206</td><td class="hits"></td><td class="source"> * @param {Buffer} body</td></tr><tr><td class="line">207</td><td class="hits"></td><td class="source"> * @param {String} charset</td></tr><tr><td class="line">208</td><td class="hits"></td><td class="source"> * @param {Boolean} hasPathIndex</td></tr><tr><td class="line">209</td><td class="hits"></td><td class="source"> * @private {Object}</td></tr><tr><td class="line">210</td><td class="hits"></td><td class="source"> */</td></tr><tr class="hit"><td class="line">211</td><td class="hits">1</td><td class="source">function _parseStorage(body, charset, hasPathIndex) {</td></tr><tr class="hit"><td class="line">212</td><td class="hits">27</td><td class="source"> var result = {}</td></tr><tr><td class="line">213</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">214</td><td class="hits">27</td><td class="source"> var group = helpers.trim(body.toString(charset, 0, protocol.FDFS_GROUP_NAME_MAX_LEN))</td></tr><tr class="hit"><td class="line">215</td><td class="hits">27</td><td class="source"> var ip = helpers.trim(body.toString(charset, protocol.FDFS_GROUP_NAME_MAX_LEN, protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1))</td></tr><tr class="hit"><td class="line">216</td><td class="hits">27</td><td class="source"> var port = Number('0x' + body.toString('hex',</td></tr><tr><td class="line">217</td><td class="hits"></td><td class="source"> protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1,</td></tr><tr><td class="line">218</td><td class="hits"></td><td class="source"> protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE))</td></tr><tr><td class="line">219</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">220</td><td class="hits">27</td><td class="source"> result.group = group</td></tr><tr class="hit"><td class="line">221</td><td class="hits">27</td><td class="source"> result.host = ip</td></tr><tr class="hit"><td class="line">222</td><td class="hits">27</td><td class="source"> result.port = port</td></tr><tr><td class="line">223</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">224</td><td class="hits">27</td><td class="source"> if (hasPathIndex &&</td></tr><tr><td class="line">225</td><td class="hits"></td><td class="source"> body.length > protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE) {</td></tr><tr class="hit"><td class="line">226</td><td class="hits">8</td><td class="source"> var storePathIndex = Number('0x' + body.toString('hex',</td></tr><tr><td class="line">227</td><td class="hits"></td><td class="source"> protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE,</td></tr><tr><td class="line">228</td><td class="hits"></td><td class="source"> protocol.FDFS_GROUP_NAME_MAX_LEN + protocol.FDFS_IPADDR_SIZE - 1 + protocol.FDFS_PROTO_PKG_LEN_SIZE + 1))</td></tr><tr><td class="line">229</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">230</td><td class="hits">8</td><td class="source"> result.storePathIndex = storePathIndex</td></tr><tr><td class="line">231</td><td class="hits"></td><td class="source"> }</td></tr><tr><td class="line">232</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">233</td><td class="hits">27</td><td class="source"> return result</td></tr><tr><td class="line">234</td><td class="hits"></td><td class="source">}</td></tr><tr><td class="line">235</td><td class="hits"></td><td class="source"> </td></tr><tr class="hit"><td class="line">236</td><td class="hits">1</td><td class="source">module.exports = Tracker</td></tr><tr><td class="line">237</td><td class="hits"></td><td class="source"> </td></tr></tbody></table></div></div></div></body></html>