3

アプリの一部は Ruby で記述され、その他は node.js を使用して記述されています。

zlib チャンクを格納する redis ストアを使用して、それらの間でデータを共有します。ノードを使用して次のコードで書き込みます。

zlib.deflate(xml.toString(), function(error, deflated) {
  ...
  deflated.toString('binary'); // That's the string we write in Redis
  ...
});

ここで、Ruby (1.8.7) を使用して redis ストアでこのデータを読み取りましたが、その方法がわかりません。

ストアから取得する典型的な文字列は次のようになります。

=> "xuAo \020ÿ\ná.£v½\030dÿCO½±:«¤(\004ƪÿ¾¬®5MÚ\003÷½IÞ q¤°²e°c¼òÈ×\000ó<ùM¸ÐAç\025ÜÈ\r|gê\016Ý/.é\020ãÆî×\003Ôç<Ýù2´F\n¨Å\020!zl \0209\034p|üÀqò\030\036m\020\e`\031¼ÏütÓ=ø¦U/ÔO±\177zB{\037½£-ðBu©ò¢X\000kb­*Ó[V\024Y^½EÎ¥üpúrò­¦\177ÁÃdÈ¢j\0353$a\027²q#¥]*Ýi3J8¤´füd\eså[³öʵ%\fcÇY\037ð¬ÿg§í^¥8£Õ§a¶\001=\r;¡¾\001\020Pí" 

もちろん、使用してみ Zlib::Inflate.new.inflate(compressed)ましたが、 . で失敗しますZlib::DataError: incorrect header check

Ruby から文字列を膨らませるために、その文字列にどのような変換を行う必要があるかについて何か考えはありますか?

PS: ノードからインフレートするのは簡単で機能するので、問題は圧縮方法ではありません。

4

3 に答える 3

5

Ruby から文字列を膨らませるために、その文字列にどのような変換を行う必要があるかについて何か考えはありますか?

UTF-8 から Latin-1 へ

理想的には、ノード側で直接バッファを操作する限り、変換の必要はありません。一番下にある Node と Ruby のコード ブロックのペアを参照してください。ただし、問題の本質は、これに対処するために Ruby 側だけで何ができるかということです。

Ruby のみ - UTF-8 から LATIN-1 に変換

require 'zlib'
require 'rubygems'
require 'redis'
require 'iconv'

redis = Redis.new

def inflate(buffer)
    zstream = Zlib::Inflate.new
    buf = zstream.inflate(buffer)
    zstream.finish
    zstream.close
    buf
end


def convert(buffer)
    utf8_to_latin1 = Iconv.new("LATIN1//TRANSLIT//IGNORE", "UTF8")
    utf8_to_latin1.iconv(buffer) 
end

value = redis.get("testkey")
value = convert(value)
puts inflate(value);

説明

上記のコードは iconv を使用して、Redis から取得した値を UTF-8 から目的のバイトに戻します。

ノードでデフレートすると、結果のバッファには正しい zlib 生成バイトが含まれます。からの結果文字列toString('binary')、文字ごとの文字は、deflate 結果バッファの内容にも一致します。ただし、deflate の結果が Redis に保存されるまでには、UTF-8 でエンコードされます。例:

文字列 "ABCABC" を収縮すると、次のようになります。

<Buffer 78 9c 73 74 72 76 74 72 06 00 05 6c 01 8d>

それでも、Redis は以下を返します。

<Buffer 78 c2 9c 73 74 72 76 74 72 06 00 05 6c 01 c2 8d>

少し仮説を立てると、結果の文字列はtoString('binary')、おそらく node-redis のどこかで new Buffer(...) への引数として終わるようです。new Buffer() にエンコーディング引数が指定されていない場合、デフォルトの UTF-8 エンコーディングが適用されます。(最初の参照を参照してください)。さらに仮説を立てると、バッファーのみを使用することで、文字列からバッファーを作成する必要がなくなり、その結果、UTF8 エンコーディングが回避され、Redis の内外で正しい deflate 値が作成されます。

参考文献

ノード

var zlib = require('zlib');
var redis = require("redis").createClient();

var message = new Buffer('your stuff goes here.');
//var message = new Buffer(xml.toString());

redis.on("error", function (err) {
console.log("Error " + err);
});

redis.on("connect", function() {
    console.log(message);
    zlib.deflate(message, function(error, deflated) {
        console.log(deflated);          
        redis.set("testkey",deflated,function (err, reply) {
            console.log(reply.toString());
        });
    });
});

ルビー

require 'zlib'
require 'rubygems'
require 'redis'

redis = Redis.new

def inflate(buffer)
    zstream = Zlib::Inflate.new
    buf = zstream.inflate(buffer)
    zstream.finish
    zstream.close
    buf
end

value = redis.get("testkey")    

puts inflate(value)
于 2012-11-14T09:46:49.927 に答える
2

データの保存に node-redis を使用している場合は、Buffers を直接処理するため、単純に client.set(key, buff) または client.append(key, buff) を実行できるため、その必要はありません (したくありません)。任意の変換を行います。

Node.js (Kevin から簡略化)

var zlib = require('zlib');
var redis = require("redis");
var rc = redis.createClient(null, null, {detect_buffers: true}); // allow Buffers

var message = new Buffer('My message');

zlib.deflate(message, function (err, deflated) {
  if (err) return console.error(err);
  rc.set("testkey", deflated, function (err, result) {
    if (err) return console.error(err);
    rc.quit();
  });
});

Ruby コード (上記の Kevin からコピー)

require 'zlib'
require 'rubygems'
require 'redis'

redis = Redis.new

def inflate(buffer)
    zstream = Zlib::Inflate.new
    buf = zstream.inflate(buffer)
    zstream.finish
    zstream.close
    buf
end

value = redis.get("testkey")

puts inflate(value)

それは値を適切に取得することで機能しますが、Node.jsコードを使用する.toString('binary')ように変更すると、前述のようにRubyのデコードが最初に壊れます。

toString('binary') がデータをいじることを示す例を次に示します

 console.log(deflated);
 console.log(new Buffer(deflated.toString('binary')));

したがって、 Buffer.toString('binary') が V8 Buffer コードに入ると信じているため、 Buffer.toString('binary') がどのような変換を行っているのかわかりません。

しかし、Node.js でまだそれを読み取ることができる場合は.toString('binary')、バッファを redis クライアント set メソッドに渡すだけで適切に保存されます。

すると、バイナリとして保存され、上記のようなコードを使用して ruby​​ で正しく読み取ることができます。

node.js コードについては、バイナリとして適切に保存したら (set 呼び出しで直接 Buffer を使用)、それを取得するには:

var rc = redis.createClient(null, null, {detect_buffers: true}); // allow Buffers
rc.get(new Buffer(key), function (err, buff) {  // use a Buffer for the key
   // buff is a Buffer now
});

node-redis に対して detect_buffers をオンにすることで、Buffer をキーとして渡すと、Buffer として取得され、変換されません。

別の方法としてreturn_buffers = trueオプションをdetect_buffers使用することもできますが、バッファ データと非バッファ データの両方に同じクライアントを使用できるようにしたいと思います。

PS。Ruby gem が 1.x (2.x でバイナリ修正が追加された) のような古いバージョンではなく、最新バージョンのいずれかを使用していることを確認してください。

于 2012-11-15T00:59:29.163 に答える
0

使って回心するという行為は、toStringすでにあなたを罪の状態に置いています。Ruby の inflate でデコードできるようにするには、deflate で生成された元のバイナリ バッファーを変換せずに保存して送信する必要があります。

変換が何をするのかは明確ではありません'binary'が、おそらく null を削除するため、データが台無しになります。いずれにせよ、ドキュメントには、binary使用すべきではなく、非推奨であると記載されています。元の圧縮されたデータを Buffer クラスで直接渡す方法を見つける必要があります。または、文字列が本当に必要な場合は、膨張を試みる前に Ruby で元に戻すことができる文字列形式に変換する必要があります。たとえばbase64。

于 2012-11-05T23:53:19.783 に答える