├── README.md
├── img
└── arch.jpg
└── src
├── App-simulation.js
├── App-simulation2.js
├── caconfig
└── ws_nodejs
│ └── fabric_sdk_node_studynew
│ └── fabric-client-kvs
│ └── Admin
├── config.js
├── fabricService.js
├── package.json
├── server.js
├── socket-server.js
└── test.js
/README.md:
--------------------------------------------------------------------------------
1 | 一个Fabric+IPFS数据上链的Demo
2 |
3 | 完整方案文档:[基于Fabric+IPFS大规模数据上链方案](https://www.wanghaoyi.com/fabric-ipfs-technical-solution.html)
4 |
5 |
6 |
7 | **Fabric简介、适用场景**:Hyperledger Fabric的出现是对传统区块链模型的一种革新,在某种程度上允许创建授权和非授权的区块链,Hyperledger还通过提供一个针对身份识别,可审计、隐私安全和健壮的模型,使得缩短计算周期、提高规模效率和响应各个行业的应用需求成为可能。
8 | **IPFS简介、适用场景**:分布式存储。
9 | **Fabric+IPFS优点、适用场景**:无需全部数据上链即可产生信任。
10 | 本系统以后台服务开发为核心,作为链接客户端、IPFS及Fabric区块链的服务部件。当大量数据需要可靠实时地存储,并且在未来需要得到验证时,必须将数据以某种形式存入区块链。而传统区块链系统为了“安全”而牺牲“效率”,因此其数据存储的容量与速率非常低下,因此不能存放大规模数据。基于这种考虑,我们可以利用区块链+分布式存储的方式解决大规模数据上链的问题,将原始数据存于类似IPFS等分布式系统中,并将源文件的地址存储于区块链永久保存,用户可以通过区块链上文件的地址信息随时去获取这些数据。同时为了保证IPFS上数据不被篡改,必须将文件的指纹(Hash算法结果)也一并存入区块链,这样用户可以将得到的链上数据进行验证,以确定数据的完整性与可靠性。
--------------------------------------------------------------------------------
/img/arch.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atwhy/dataCompressWithFabricAndIPFS/ca46930e58807ea33160e0d75b13be6a68785aff/img/arch.jpg
--------------------------------------------------------------------------------
/src/App-simulation.js:
--------------------------------------------------------------------------------
1 | var http=require("http");
2 | var stringRandom = require('string-random');
3 | var async = require('async');
4 | const sendItems = 10;
5 |
6 |
7 | var tamplateDataOfOBD ={
8 |
9 | // datanum:0,
10 | timeStamp:0,
11 | odbid:0,
12 | tirepressure:0,
13 | distance:0,
14 | carspeed:0,
15 | enginespeed:0,
16 | gasremain:0,
17 | enginetemperature:0,
18 | waterbox:0,
19 | localtion:0
20 | };
21 |
22 | var optionsPost={
23 | hostname:"127.0.0.1",
24 | port:60003,
25 | path:'/obddata',
26 | json: true,
27 | method:"POST",
28 | headers: {
29 | 'Content-Type': 'application/json'
30 | }
31 | };
32 |
33 |
34 | let count = 1;
35 |
36 | async.whilst(
37 | function(){
38 | return count <= sendItems;
39 | },
40 | function(cb){
41 |
42 | var req = http.request(optionsPost,function(res){
43 | res.on("data",function(chunk){
44 | console.log("received chunck:",chunk.toString());
45 | });
46 | res.on("end",function(){
47 | console.log("### end ##");
48 | });
49 | console.log("res.statusCode:",res.statusCode);
50 | });
51 |
52 | req.on("error",function(err){
53 | console.log(err.message);
54 | });
55 |
56 | // tamplateDataOfOBD.datanum = count;
57 | tamplateDataOfOBD.timeStamp = new Date();
58 | tamplateDataOfOBD.odbid = 'X7777-S6668';
59 | tamplateDataOfOBD.tirepressure = stringRandom(4, {letters: false});
60 | tamplateDataOfOBD.distance = stringRandom(4, {letters: false});
61 | tamplateDataOfOBD.carspeed = stringRandom(4, {letters: false});
62 | tamplateDataOfOBD.enginespeed = stringRandom(4, {letters: false});
63 | tamplateDataOfOBD.gasremain = stringRandom(4, {letters: false});
64 | tamplateDataOfOBD.enginetemperature = stringRandom(4, {letters: false});
65 | tamplateDataOfOBD.waterbox = stringRandom(4, {letters: false});
66 | tamplateDataOfOBD.localtion = stringRandom(4, {letters: false});
67 | console.log(tamplateDataOfOBD);
68 | req.write(JSON.stringify(tamplateDataOfOBD));
69 | req.end();
70 |
71 | count++;
72 | setTimeout(cb, 500);
73 | },
74 | function(err){
75 | console.log(err);
76 | }
77 | );
78 |
79 |
--------------------------------------------------------------------------------
/src/App-simulation2.js:
--------------------------------------------------------------------------------
1 | var http=require("http");
2 | var querystring = require("querystring");
3 | var stringRandom = require('string-random');
4 | var async = require('async');
5 | const sendItems = 10;
6 |
7 |
8 | var tamplateDataOfOBD ={
9 |
10 | datanum:0,
11 | timeStamp:0,
12 | odbid:0,
13 | tirepressure:0,
14 | distance:0,
15 | carspeed:0,
16 | enginespeed:0,
17 | gasremain:0,
18 | enginetemperature:0,
19 | waterbox:0,
20 | localtion:0
21 | };
22 |
23 | var optionsPost={
24 | hostname:"127.0.0.1",
25 | port:60003,
26 | path:'/obddata',
27 | json: true,
28 | method:"POST",
29 | headers: {
30 | 'Content-Type': 'application/json'
31 | }
32 | };
33 |
34 |
35 |
36 | let count = 1;
37 |
38 | async.whilst(
39 | function(){
40 | return count <= sendItems;
41 | },
42 | function(cb){
43 |
44 | var req = http.request(optionsPost,function(res){
45 | res.on("data",function(chunk){
46 | console.log("received chunck:",chunk.toString());
47 | });
48 | res.on("end",function(){
49 | console.log("### end ##");
50 | });
51 | console.log("res.statusCode:",res.statusCode);
52 | });
53 |
54 | req.on("error",function(err){
55 | console.log(err.message);
56 | });
57 |
58 | tamplateDataOfOBD.datanum = count;
59 | tamplateDataOfOBD.timeStamp = new Date();
60 | tamplateDataOfOBD.odbid = 'X8888-S6665';
61 | tamplateDataOfOBD.tirepressure = stringRandom(4, {letters: false});
62 | tamplateDataOfOBD.distance = stringRandom(4, {letters: false});
63 | tamplateDataOfOBD.carspeed = stringRandom(4, {letters: false});
64 | tamplateDataOfOBD.enginespeed = stringRandom(4, {letters: false});
65 | tamplateDataOfOBD.gasremain = stringRandom(4, {letters: false});
66 | tamplateDataOfOBD.enginetemperature = stringRandom(4, {letters: false});
67 | tamplateDataOfOBD.waterbox = stringRandom(4, {letters: false});
68 | tamplateDataOfOBD.localtion = stringRandom(4, {letters: false});
69 | console.log(tamplateDataOfOBD);
70 | req.write(JSON.stringify(tamplateDataOfOBD));
71 | req.end();
72 |
73 | count++;
74 | setTimeout(cb, 3000);
75 | },
76 | function(err){
77 | console.log(err);
78 | }
79 | );
80 |
81 |
--------------------------------------------------------------------------------
/src/caconfig/ws_nodejs/fabric_sdk_node_studynew/fabric-client-kvs/Admin:
--------------------------------------------------------------------------------
1 | {"name":"Admin","mspid":"Org1MSP","roles":null,"affiliation":"","enrollmentSecret":"","enrollment":{"signingIdentity":"9edbf3ff227c9efe9cc36febff379d0d1e1fec30787a1edd0dbbfe1fb65c04d8","identity":{"certificate":"-----BEGIN CERTIFICATE-----\nMIICEzCCAbqgAwIBAgIRAN1+ehQKsifIdcQrUeEPmxwwCgYIKoZIzj0EAwIwbzEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xFzAVBgNVBAoTDm9yZzEudHJhY2UuY29tMRowGAYDVQQDExFjYS5v\ncmcxLnRyYWNlLmNvbTAeFw0xOTAzMDYwODExNTFaFw0yOTAzMDMwODExNTFaMFkx\nCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4g\nRnJhbmNpc2NvMR0wGwYDVQQDDBRBZG1pbkBvcmcxLnRyYWNlLmNvbTBZMBMGByqG\nSM49AgEGCCqGSM49AwEHA0IABGOcgkhXYeVU5v9ofV/O29bjQCu8hYecz9ClCDwy\nuPrdKAM+1dIO0ppstnsgNIJOjxo8zn4e54cGXBlwkKQLqP2jTTBLMA4GA1UdDwEB\n/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIJcCDKTW/wM4aS+f7zhy\nuVLkECa2Vt8MMAdsHPRQ3LP2MAoGCCqGSM49BAMCA0cAMEQCIHM0t03R7TGlT9Gc\nbUdOhfCSkFriI9fjDo2fmvYfSoueAiAtyDGyHylrJXeUFCA1ywsjPbuQRaXoTPd5\nXR8WW9AxZw==\n-----END CERTIFICATE-----\n"}}}
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | const blockItemsNum = 100;
2 |
3 | module.exports = {
4 | blockItemsNum : blockItemsNum
5 | }
--------------------------------------------------------------------------------
/src/fabricService.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var fs = require('fs');
3 | var util = require('util');
4 | var hfc = require('fabric-client');
5 |
6 | var channelid = "cc_sendhashtofabric";
7 | var tempdir = "./caconfig/nodejs/fabric_sdk_node/fabric-client-kvs";
8 |
9 | var channelmap = {};
10 | var peermap = {};
11 | var channelpeer = {};
12 |
13 | let client = new hfc();
14 | var channel = getchannel(channelid);
15 | var order1 = client.newOrderer('grpc://172.27.43.200:7050');
16 | var order2 = client.newOrderer('grpc://172.27.34.202:7050');
17 | var order3 = client.newOrderer('grpc://172.27.34.203:7050');
18 | channel.addOrderer(order1);
19 | channel.addOrderer(order2);
20 | channel.addOrderer(order3);
21 |
22 | var peer3 = client.newPeer('grpc://172.27.43.203:7051');
23 |
24 | setupchannel(channel,peer3,channelid,"peer3");
25 | peermap["peer3"] = peer3;
26 |
27 |
28 |
29 | /**
30 | * 查询交易
31 | * @param chaincodeid
32 | * @param func
33 | * @param chaincode_args
34 | * @returns {Promise}
35 | */
36 | var queryCc = function (chaincodeid , func , chaincode_args ) {
37 |
38 | return getOrgUser4Local().then(( user )=>{
39 |
40 | var tx_id = client.newTransactionID();
41 | var request = {
42 | chaincodeId: chaincodeid,
43 | txId: tx_id,
44 | fcn: func,
45 | args: chaincode_args
46 | };
47 |
48 | return channel.queryByChaincode( request , peer3 );
49 |
50 | } ,(err)=>{
51 |
52 | console.log('error', err);
53 |
54 | } ).then(( sendtransresult )=>{
55 |
56 | return sendtransresult;
57 |
58 | },(err)=>{
59 | console.log('error', err);
60 | });
61 |
62 | }
63 |
64 |
65 | /**
66 | * 发起一笔交易
67 | *
68 | * @returns {Promise.}
69 | */
70 | var sendTransaction = function ( chaincodeid , func , chaincode_args ) {
71 | var transactionID;
72 | var tx_id = null;
73 | return getOrgUser4Local().then((user)=>{
74 | tx_id = client.newTransactionID();
75 | var request = {
76 | chaincodeId: chaincodeid,
77 | fcn: func,
78 | args: chaincode_args,
79 | chainId: channelid,
80 | txId: tx_id
81 | };
82 | return channel.sendTransactionProposal(request);
83 | } ,(err)=>{
84 | console.log('error', err);
85 | } ).then((chaincodeinvokresult )=>{
86 |
87 | var proposalResponses = chaincodeinvokresult[0];
88 | var proposal = chaincodeinvokresult[1];
89 | var header = chaincodeinvokresult[2];
90 | var all_good = true;
91 |
92 | for (let i in proposalResponses) {
93 |
94 | let one_good = false;
95 | if (proposalResponses && proposalResponses[0].response &&
96 | proposalResponses[0].response.status === 200) {
97 | one_good = true;
98 | console.info('transaction proposal was good');
99 | } else {
100 | console.error('transaction proposal was bad');
101 | }
102 | all_good = all_good & one_good;
103 | }
104 |
105 | if (all_good) {
106 |
107 | console.info(util.format(
108 |
109 | 'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s',
110 | proposalResponses[0].response.status, proposalResponses[0].response.message,
111 | proposalResponses[0].response.payload, proposalResponses[0].endorsement
112 | .signature));
113 |
114 | var request = {
115 | proposalResponses: proposalResponses,
116 | proposal: proposal,
117 | header: header
118 | };
119 | // set the transaction listener and set a timeout of 30sec
120 | // if the transaction did not get committed within the timeout period,
121 | // fail the test
122 | transactionID = tx_id.getTransactionID();
123 |
124 | return channel.sendTransaction(request);
125 | }
126 |
127 | },(err)=>{
128 | console.log('error', err);
129 | }).then(( sendtransresult )=>{
130 | if(sendtransresult){
131 | sendtransresult.txid = transactionID;
132 | }
133 | return sendtransresult; // 链码真实返回的信息
134 |
135 | },(err)=>{
136 | console.log('error', err);
137 | return err;
138 |
139 | });
140 | }
141 |
142 | /**
143 | *
144 | * 根据cryptogen模块生成的账号通过Fabric接口进行相关的操作
145 | *
146 | * @returns {Promise.}
147 | *
148 | */
149 | function getOrgUser4Local() {
150 |
151 | //测试通过CA命令行生成的证书依旧可以成功的发起交易
152 | let keyPath = "./caconfig/keystore";
153 | let keyPEM = Buffer.from(readAllFiles(keyPath)[0]).toString();
154 | let certPath = "./caconfig/signcerts";
155 | let certPEM = readAllFiles(certPath)[0].toString();
156 |
157 |
158 | return hfc.newDefaultKeyValueStore({
159 |
160 | path:tempdir
161 |
162 | }).then((store) => {
163 | client.setStateStore(store);
164 |
165 | return client.createUser({
166 | username: 'Admin',
167 | mspid: 'Org1MSP',
168 | cryptoContent: {
169 | privateKeyPEM: keyPEM,
170 | signedCertPEM: certPEM
171 | }
172 | });
173 | });
174 | }
175 |
176 | function readAllFiles(dir) {
177 | var files = fs.readdirSync(dir);
178 | var certs = [];
179 | files.forEach((file_name) => {
180 | let file_path = path.join(dir,file_name);
181 | let data = fs.readFileSync(file_path);
182 | certs.push(data);
183 | });
184 | return certs;
185 | }
186 |
187 | /**
188 | *
189 | * @param _tempdir
190 | * @param _admin_key
191 | * @param _admin_sert
192 | */
193 | var inits = function ( _tempdir , _admin_key , _admin_sert ) {
194 | tempdir = _tempdir;
195 | admin_key = _admin_key;
196 | admin_sert = _admin_sert;
197 | }
198 |
199 | /**
200 | *
201 | * @param channelid
202 | * @param peerRequest
203 | * @returns {Promise}
204 | */
205 | var getBlockChainInfo = function( channelid , peerRequest ){
206 |
207 | let channel = getchannel(channelid);
208 | let peer = getpeer(peerRequest);
209 |
210 | setupchannel(channel,peer,channelid,peerRequest);
211 |
212 | return getOrgUser4Local().then((user)=>{
213 |
214 | return channel.queryInfo(peer);
215 |
216 | } ,(err)=>{
217 |
218 | console.log( 'error' , err );
219 | } )
220 |
221 | }
222 |
223 | /**
224 | * 根据区块链的编号获取区块的详细信息
225 | *
226 | * @param blocknum
227 | * @returns {Promise.}
228 | *
229 | */
230 | var getblockInfobyNum = function (channelid , peerRequest ,blocknum) {
231 |
232 | let channel = getchannel(channelid);
233 | let peer = getpeer(peerRequest);
234 |
235 |
236 | setupchannel(channel,peer,channelid,peerRequest);
237 |
238 |
239 | return getOrgUser4Local().then((user)=>{
240 |
241 | return channel.queryBlock(blocknum, peer,null);
242 |
243 | } ,(err)=>{
244 | console.log( 'error' , err);
245 | } )
246 |
247 | }
248 |
249 | /**
250 | * 根据区块链的哈希值获取区块的详细信息
251 | *
252 | * @param blocknum
253 | * @returns {Promise.}
254 | *
255 | */
256 | var getblockInfobyHash = function ( channelid , peerRequest , blockHash ) {
257 |
258 | let channel = getchannel(channelid);
259 | let peer = getpeer(peerRequest);
260 | setupchannel(channel,peer,channelid,peerRequest);
261 |
262 | return getOrgUser4Local().then(( user )=>{
263 |
264 | return channel.queryBlockByHash(new Buffer(blockHash,"hex"),peer)
265 |
266 | } ,(err)=>{
267 |
268 | console.log('error', err);
269 |
270 | } )
271 |
272 | }
273 |
274 |
275 |
276 | /**
277 | *
278 | * 获取当前Peer节点加入的通道信息
279 | *
280 | * @param blocknum
281 | * @returns {Promise.}
282 | *
283 | */
284 | var getPeerChannel = function ( peerRequest ) {
285 |
286 |
287 | let peer = getpeer(peerRequest);
288 |
289 | return getOrgUser4Local().then(( user )=>{
290 |
291 | return client.queryChannels(peer)
292 |
293 | } ,(err)=>{
294 |
295 | console.log('error', err);
296 | } )
297 |
298 | }
299 |
300 | /**
301 | *
302 | * 查询指定peer节点已经install的chaincode
303 | *
304 | * @param blocknum
305 | * @returns {Promise.}
306 | *
307 | */
308 | var getPeerInstallCc = function ( peerRequest ) {
309 |
310 | let peer = getpeer(peerRequest);
311 |
312 | return getOrgUser4Local().then(( user )=>{
313 |
314 | return client.queryInstalledChaincodes(peer)
315 |
316 | } ,(err)=>{
317 |
318 | console.log('error', err);
319 | } )
320 |
321 | }
322 |
323 | /**
324 | *
325 | * 查询指定channel中已经实例化的Chaincode
326 | *
327 | * @param blocknum
328 | * @returns {Promise.}
329 | *
330 | */
331 | var getPeerInstantiatedCc = function ( channelid , peerRequest ) {
332 |
333 | let channel = getchannel(channelid);
334 | let peer = getpeer(peerRequest);
335 | setupchannel(channel,peer,channelid,peerRequest);
336 |
337 | return getOrgUser4Local().then(( user )=>{
338 |
339 | return channel.queryInstantiatedChaincodes( peer )
340 |
341 | } ,(err)=>{
342 |
343 | console.log('error', err);
344 |
345 | } )
346 |
347 | }
348 |
349 |
350 | var getTransaction = function (channelid , peerRequest ,transhash) {
351 |
352 | let channel = getchannel(channelid);
353 | let peer = getpeer(peerRequest);
354 | setupchannel(channel,peer,channelid,peerRequest);
355 |
356 | return getOrgUser4Local().then( (user)=>{
357 |
358 | return channel.queryTransaction(transhash, peer);
359 |
360 | },(err)=>{
361 | console.log('error',err);
362 | })
363 |
364 | }
365 |
366 | function getchannel( channel_id ) {
367 |
368 | if( channelmap[channel_id] == null){
369 | let channel = client.newChannel(channel_id);
370 | channelmap[channel_id]=channel;
371 | }
372 | return channelmap[channel_id];
373 | }
374 |
375 |
376 | function getpeer(peerRequest) {
377 |
378 | if( peermap[peerRequest] == null){
379 | let peer = client.newPeer(peerRequest);
380 | peermap[peerRequest]=peer;
381 | }
382 |
383 | return peermap[peerRequest];
384 | }
385 |
386 |
387 | function setupchannel( channel1, peer ,channel_id , peer_request) {
388 |
389 | let pkey = channel_id + peer_request;
390 |
391 | if( channelpeer[pkey] == null ){
392 | channel1.addPeer(peer);
393 | channelpeer[pkey] = peer;
394 | }
395 | }
396 |
397 | exports.inits = inits;
398 | exports.getBlockChainInfo = getBlockChainInfo;
399 | exports.getblockInfobyNum = getblockInfobyNum;
400 | exports.getblockInfobyHash = getblockInfobyHash;
401 | exports.getPeerChannel = getPeerChannel;
402 | exports.getPeerInstallCc = getPeerInstallCc;
403 | exports.getPeerInstantiatedCc = getPeerInstantiatedCc;
404 | exports.getTransaction = getTransaction;
405 | exports.sendTransaction = sendTransaction;
406 | exports.queryCc = queryCc;
--------------------------------------------------------------------------------
/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "datacompresswithfabricandipfs",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "server.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "async": "^2.6.2",
13 | "body-parser": "^1.18.3",
14 | "crypto": "^1.0.1",
15 | "express": "^4.16.4",
16 | "fabric-ca-client": "^1.0.0",
17 | "fabric-client": "^1.0.0",
18 | "ipfs-api": "^26.1.2",
19 | "net": "^1.0.2",
20 | "querystring": "^0.2.0",
21 | "string-random": "^0.1.3",
22 | "validator": "^10.11.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/server.js:
--------------------------------------------------------------------------------
1 | const Readable = require('stream').Readable;
2 | const validator = require('validator');
3 | const ipfsAPI = require('ipfs-api');
4 | const ipfs = ipfsAPI('localhost', '5001', {protocol: 'http'});
5 | const express = require('express');
6 | const crypto = require('crypto');
7 | const bodyparse = require('body-parser');
8 | const fabricService = require('./fabricService');
9 | const app = express();
10 | app.use(bodyparse.urlencoded({extend:false}));
11 |
12 | const chaincodeName = 'cc_sendhashtofabric'; // fabric链码名称
13 | const fileHashType = 'md5'; // 文件指纹类型'md5'/'sha256'
14 | const blockItems = 10080; // 每个数据块含有数据条目
15 |
16 | var obdidArrays = new Set();
17 | var obdidCount = {};
18 | var streamArrays = {};
19 | var currentStream = null;
20 | var currentHashHandler = null;
21 | var hashComputeHandlers = {};
22 |
23 | // 自定义可读流
24 | class MyReadable extends Readable{
25 | constructor(dataSource, options){
26 | super(options);
27 | }
28 | _read(){
29 | const data = "";
30 | this.push(data)
31 | }
32 | }
33 |
34 | // 向IPFS发送打包后的块
35 | function sendBlockDataToIPFS(readableStream) {
36 | //向IPFS发送数据块
37 | return new Promise(function (resolve,reject) {
38 | ipfs.util.addFromStream(readableStream).then(result => {
39 | console.log("sendBlockDataToIPFS ==> IPFS return: \n" ,result)
40 | resolve(result);
41 | }).catch(e => {
42 | console.log("sendBlockDataToIPFS ==> err is:" ,e)
43 | reject(e);
44 | });
45 | });
46 | }
47 |
48 | // 向fabric发送数据块的相关信息:日期、IPFS地址、文件指纹
49 | // 格式:{obdid:xxx,date:2019-4-1,ipfsAdd:xxx,fileFingerprint:xxx}
50 | function sendHashToBlockChain(obdId,ipfsAddr,fileFingerprint){
51 | let sendJson = {
52 | 'dataDate':new Date().toLocaleDateString(),
53 | 'ipfsAddr':ipfsAddr,
54 | 'fileFingerprint':fileFingerprint
55 | };
56 | return new Promise(function (resolve,reject) {
57 | fabricService.sendTransaction(chaincodeName,"invoke",["putvalue",obdId,JSON.stringify(sendJson)]).then(fabricHash => {
58 | console.log("sendHashToBlockChain ==> fabricHash is: " ,fabricHash)
59 | resolve(fabricHash)
60 | }).catch(e => {
61 | console.log("sendHashToBlockChain ==> has a err: " ,e)
62 | reject(e)
63 | });
64 | });
65 | }
66 |
67 | // 创建流,以流方式向IPFS传输数据
68 | var streamInit = function (){
69 | let myReadable = new MyReadable();
70 | myReadable.setEncoding('utf8');
71 |
72 | myReadable.on('data', (chunk) => {
73 | console.log(`接收到 ${chunk.length} 个字节的数据`);
74 | });
75 | myReadable.on('end', (chunk) => {
76 | console.log('已没有数据');
77 | });
78 | myReadable.on('close', (chunk) => {
79 | console.log('流已关闭');
80 | });
81 | return myReadable;
82 | };
83 |
84 |
85 | /***
86 | * 客户端上传数据处理,使用http-post方式
87 | */
88 | app.post("/obddata",bodyparse.json(),function (req,res) {
89 |
90 | let dataJson = req.body;
91 | let dataString = JSON.stringify(dataJson);
92 | let storedDataString = dataString +"\n" ;
93 | console.log("typeof dataJson:",typeof dataJson);
94 | console.log("typeof dataString:",typeof dataString);
95 |
96 | if(validator.isJSON(dataString)) {
97 | console.log("validator.isJSON is true!");
98 | if (!obdidArrays.has(dataJson.odbid)) { // 新连接的客户端
99 | console.log("a new client!");
100 | currentStream = streamInit();
101 | currentStream.push(storedDataString);// json字符串全放入Readable
102 | currentStream.pause();
103 | streamArrays[dataJson.odbid] = currentStream;
104 |
105 | currentHashHandler = crypto.createHash(fileHashType).update(storedDataString);
106 | hashComputeHandlers[dataJson.odbid] = currentHashHandler;
107 |
108 | obdidArrays.add(dataJson.odbid);// 查重集合
109 | obdidCount[dataJson.odbid] = 1; // 开始计数
110 |
111 | } else{ // 已经连接的客户端
112 | console.log("a already connected client!");
113 | currentStream = streamArrays[dataJson.odbid];
114 | currentStream.push(storedDataString);// 全放入Readable
115 | currentStream.pause();
116 | obdidCount[dataJson.odbid] += 1;
117 | hashComputeHandlers[dataJson.odbid].update(storedDataString);// 更新MD5/SHA1
118 | }
119 |
120 | if (obdidCount[dataJson.odbid] == blockItems) { // 最后一条数据完成并开始打包数据块,以Json字符串存储
121 | console.log("package:", dataJson.odbid);
122 | currentStream = streamArrays[dataJson.odbid];
123 | currentStream.resume();
124 | currentStream.push(null);
125 |
126 | // 计算该块的hash
127 | currentHashHandler = hashComputeHandlers[dataJson.odbid];
128 | let blockHash = currentHashHandler.digest('hex').toUpperCase();
129 | console.log("the block hash is:", blockHash);
130 |
131 | // 向IPFS发送打包数据块
132 | sendBlockDataToIPFS(currentStream).then(ipfsHash => {
133 | console.log("the ipfsHash is:", ipfsHash[0].hash);
134 | // 向Fabric发送数据存储的地址以及数据指纹
135 | console.log("the fileFingerprint is:", blockHash);
136 | return sendHashToBlockChain(dataJson.odbid, ipfsHash[0].hash, blockHash);
137 |
138 | }).then(txhash => {
139 | if (txhash) {
140 | console.log("the txhash is: ", txhash);
141 | }
142 | }).catch(e => {
143 | console.log(e)
144 | });
145 |
146 | // 为下一个数据块进行初始化
147 | obdidCount[dataJson.odbid] = 1;
148 | hashComputeHandlers[dataJson.odbid] = crypto.createHash('md5');
149 | currentStream.destroy();
150 | streamArrays[dataJson.odbid] = streamInit();
151 |
152 | res.write("the block finished!");
153 | res.end();
154 | }
155 | }
156 | });
157 |
158 |
159 | /***
160 | * 通过内容获取fabric链上信息,使用http-get方式
161 | */
162 | app.get('/querydata',function (req,res) {
163 | console.log("enter");
164 | let date = req.query.date;
165 | let obd = req.query.obdid;
166 | fabricService.queryCc(chaincodeName, "invoke", ["gethistory", obd, ""]).then(itemStrArry => {
167 | // res.end(itemStrArry)
168 | console.log("itemStrArry:",itemStrArry);
169 | let fabricInfo = null;
170 | let ret = {};
171 | let itemsInfo = eval(itemStrArry.toString());
172 | console.log("saved in fabric itemsInfo:", itemsInfo);
173 |
174 | if(itemsInfo !== null && itemsInfo !== undefined){
175 |
176 | for (let i = 0; i < itemsInfo.length; i++) {
177 | let dataJson = JSON.parse(itemsInfo[i]);
178 | if(date === dataJson.dataDate){
179 | fabricInfo = dataJson;
180 | break;
181 | }
182 | }
183 | ipfs.get(fabricInfo.ipfsAddr,function (err,files) {
184 | if (err || typeof files == "undefined") {
185 | console.log(err);
186 | res.end(JSON.stringify(err))
187 | } else {
188 | let content = files[0].content.toString();
189 |
190 | console.log("content:",content);
191 | ret['obdid'] = obd;
192 | ret['dataDate'] = fabricInfo.dataDate;
193 | ret['ipfsAddr'] = fabricInfo.ipfsAddr;
194 | ret['fileFingerprint'] = fabricInfo.fileFingerprint;
195 | res.end(JSON.stringify(ret))
196 | }
197 | });
198 | }
199 | })
200 | });
201 |
202 |
203 | /***
204 | * 通过hash获取IPFS上数据块,使用http-get方式
205 | */
206 | app.get('/getobject',function (req,res){
207 | let hash = req.query.hash;
208 | ipfs.object.get(hash, (err, node) => {
209 | if (err) {
210 | res.end(err);
211 | throw err;
212 | }else {
213 | // res.end(node.data.toString());
214 | res.end(node.serialized.toString());
215 | }
216 | })
217 | });
218 |
219 | /***
220 | * 通过hash获取fabric上数据块,使用http-get方式
221 | */
222 | app.get('/getfabricinfobyhash',function (req,res){
223 | fabricService.getTransaction(fabricinfo => {
224 | res.end(JSON.stringify(fabricinfo));
225 | })
226 | });
227 |
228 |
229 | //启动http服务
230 | var server = app.listen(60003, function () {
231 | var host = server.address().address;
232 | var port = server.address().port;
233 |
234 | console.log('Example app listening at http://%s:%s', host, port);
235 | });
236 |
237 | //注册异常处理器
238 | process.on('unhandledRejection', function (err) {
239 | console.error(err.stack);
240 | });
241 | process.on('uncaughtException', console.error);
242 |
--------------------------------------------------------------------------------
/src/socket-server.js:
--------------------------------------------------------------------------------
1 | const ipfsAPI = require('ipfs-api');
2 | const ipfs = ipfsAPI('localhost', '5001', {protocol: 'http'});//连接本地IPFS
3 | const buffer = Buffer.from('this is a demo');
4 |
5 | const net = require('net');
6 | const listenPort = 60001;//监听端口
7 | const fs = require('fs'); // 引入fs模块
8 | const crypto = require('crypto');
9 | const config = require('./config');
10 |
11 |
12 | // 向区块链发送Hash
13 | function sendHashToBlockChain(){
14 |
15 | }
16 | // 向IPFS发送打包后的块
17 | function sendBlockDataToIPFS(content) {
18 | const buffer = Buffer.from(content);
19 | const json = JSON.parse(content);
20 | console.log("typeof json:",typeof json)
21 | return new Promise(function (resolve,reject) {
22 | ipfs.add(content)
23 | .then( rsp => {
24 | console.log(rsp[0].hash);
25 | resolve(rsp[0].hash);
26 | }) //得到IPFS返回的hash
27 | .catch(err => console.error(err));
28 | })
29 | }
30 |
31 | // var globalWriteStream = null;
32 |
33 | // var createWriteStreamAndMonitor = function (filePath) {
34 | // globalWriteStream = fs.createWriteStream(filePath);
35 | //
36 | // globalWriteStream.on('finish',function () {
37 | // console.log()
38 | // });
39 | //
40 | // globalWriteStream.on('error',function (err) {
41 | // console.log("globalWriteStream has a err:",err)
42 | // });
43 | // };
44 | //
45 | // createWriteStreamAndMonitor(blockNumber,'../productionData/' + blockNumber + ".json");
46 | //
47 | // globalWriteStream.write(data);
48 |
49 |
50 | // 创建socket服务端
51 | var server = net.createServer(function(socket){
52 |
53 | let blockContent = null;
54 | let contentItemNumber = 0;
55 | let blockNumber = 0;
56 | let blockItems = config.blockItemsNum;
57 | let blockMd5 = "";
58 | socket.setEncoding('binary');
59 | //接收到数据
60 | socket.on('data',async function(data){
61 | // 该处不能使用阻塞方法
62 | // console.log('connect: ' + socket.remoteAddress + ':' + socket.remotePort);
63 | // console.log("data-----",data);
64 | // let dataJson = JSON.parse(data);
65 | // console.log("type of dataJson-----",typeof dataJson);
66 |
67 | contentItemNumber += 1;
68 |
69 | blockContent += data;
70 |
71 | if(contentItemNumber === blockItems){ // 本块最后一条
72 | console.log("enter if----------------------");
73 |
74 | // 计算该块的hash
75 | blockMd5 = crypto.createHash('md5').update(blockContent).digest('hex').toUpperCase();
76 | console.log("blockMd5: ",blockMd5);
77 |
78 | // 开启子进程读文件并向IPFS传输
79 | sendBlockDataToIPFS(blockContent).then((hash,err) => {
80 | if(err){
81 | console.log(err);
82 | return null;
83 | }else {
84 | console.log("sendBlockDataToIPFS hash:",hash);
85 | return ipfs.get(hash)
86 | }
87 |
88 | }).then(res => {
89 | console.log("ipfs get:",res)
90 | // sendHashToBlockChain(blockMd5,hash);
91 | });
92 |
93 | blockContent = "";
94 |
95 | }else if(contentItemNumber >= blockItems ){ // 下一块第一条
96 | contentItemNumber = 1;
97 | blockNumber += 1;
98 |
99 | }
100 |
101 | // console.log("contentItemNumber:"+contentItemNumber,+'\t'+"dataNum:"+data);
102 |
103 | await fs.writeFile('../productionData/' + blockNumber + ".json", data + '\n', { 'flag': 'a',encoding: 'utf8' }, function(err) {
104 | if (err) {
105 | throw err;
106 | }
107 | });
108 |
109 |
110 | });
111 | // socket.write('Hello client!\r\n');
112 | // socket.pipe(socket);
113 | //数据错误事件
114 | socket.on('error',function(exception){
115 | console.log('socket error:' + exception);
116 | socket.end();
117 | });
118 | //客户端关闭事件
119 | socket.on('close',function(data){
120 | console.log('client closed!');
121 | // socket.remoteAddress + ' ' + socket.remotePort);
122 | });
123 | }).listen(listenPort);
124 | //服务器监听事件
125 | server.on('listening',function(){
126 | console.log("server listening:" + server.address().port);
127 | });
128 | //服务器错误事件
129 | server.on("error",function(exception){
130 | console.log("server error:" + exception);
131 | });
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/src/test.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atwhy/dataCompressWithFabricAndIPFS/ca46930e58807ea33160e0d75b13be6a68785aff/src/test.js
--------------------------------------------------------------------------------