├── 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 --------------------------------------------------------------------------------