12

これは、ファイルをチャンクでダウンロードすることについての私の質問の続きです。説明はかなり大きいので、いくつかに分けてみます。

1)私がやろうとしたことは?

Window-Phoneアプリケーションのダウンロードマネージャーを作成していました。まず、大きなファイルをダウンロードする問題を解決しようとしました(説明は前の質問にあります)。いいえ、 「再開可能なダウンロード」機能を追加したいと思います。

2)私がすでに行ったこと。

現在、私はうまく機能しているダウンロードマネージャーを持っています。これにより、WindowsPhoneのRAMの制限を超えることができます。このマネージャーの筋書きは、HTTP Rangeヘッダーを使用して、結果的にファイルの小さなチャンクをダウンロードできるようにすることです。

それがどのように機能するかについての簡単な説明:

ファイルは一定サイズのチャンクでダウンロードされます。このサイズを「デルタ」と呼びましょう。ファイルチャンクがダウンロードされた後、それは追加モードでローカルストレージ(ハードディスク、WPでは分離ストレージと呼ばれます)に保存されます(したがって、ダウンロードされたバイト配列は常にファイルの最後に追加されます)。単一のチャンクをダウンロードした後、ステートメント

if (mediaFileLength >= delta) // mediaFileLength is a length of downloaded chunk

チェックされます。それが本当なら、それはダウンロードのために何かが残っていることを意味し、このメソッドは再帰的に呼び出されます。それ以外の場合は、このチャンクが最後であり、ダウンロードするものが残っていないことを意味します。

3)何が問題なのですか?

このロジックを1回限りのダウンロードで使用するまで(つまり、ファイルのダウンロードを開始してダウンロードが完了するまで待つ場合)、これはうまく機能していました。しかし、 「ダウンロード再開」 機能が必要だと思いました。だから、事実:

3.1)ファイルのチャンクサイズは一定であることを私は知っています。

3.2)ファイルが完全にダウンロードされたかどうかはわかります。(これは私のアプリロジックの間接的な結果であり、説明によってあなたを疲れさせることはありません。これが事実であると仮定してください)

これらの2つのステートメントを前提として、ダウンロードされたチャンクの数が (CurrentFileLength)/deltaに等しいことを証明できます。CurrentFileLenght、ダウンロード済みのファイルのサイズ(バイト単位)です。

ファイルのダウンロードを再開するには、必要なヘッダーを設定し、ダウンロードメソッドを呼び出すだけです。それは論理のようですね。そして私はそれを実装しようとしました:

    // Check file size
    using (IsolatedStorageFileStream fileStream = isolatedStorageFile.OpenFile("SomewhereInTheIsolatedStorage", FileMode.Open, FileAccess.Read))
    {
      int currentFileSize = Convert.ToInt32(fileStream.Length);
      int currentFileChunkIterator = currentFileSize / delta;
    }

そして、結果として私が見るものは何ですか?ダウンロードされたファイルの長さは2432000バイトです(デルタは304160、合計ファイルサイズは約4.5 MB、ダウンロードしたのは半分だけです)。したがって、結果は約7,995になります。(実際にはlong / int型なので、7であり、代わりに8にする必要があります!)なぜこれが発生するのですか?簡単な計算では、ファイルの長さは2433280である必要があるため、指定された値は非常に近くなりますが、等しくはありません。

さらなる調査により、から与えられたすべての値fileStream.Lengthは正確ではないが、すべてが近いことが示されました。

なぜこうなった?正確にはわかりませんが、おそらく.Length値はファイルメタデータのどこかから取得されています。おそらく、このような丸めはこの方法では正常です。おそらく、ダウンロードが中断されたとき、ファイルは完全に保存されていませんでした...(いいえ、それは本当に素晴らしいです、それはできません)

したがって、問題は設定されています-それは「ダウンロードされたチャンクの数を決定する方法」です。問題はそれをどのように解決するかです。

4)問題を解決することについての私の考え。

私の最初の考えは、ここで数学を使うことについてでした。イプシロンネイバーフッドを設定し、ステートメントで使用しcurrentFileChunkIterator = currentFileSize / delta;ます。しかし、それは私たちにタイプIとタイプIIのエラー(または統計用語が気に入らない場合は誤警報とミス)について覚えておく必要があります。おそらく、ダウンロードするものは何も残っていません。また、提供値と真の値の差が恒久的に大きくなるのか、周期的に変動するのかは確認していません。小さいサイズ(約4-5 MB)では、成長しか見られませんでしたが、それは何も証明していません。

ですから、私は自分の解決策が気に入らないので、ここで助けを求めています。

5)答えとして聞きたいこと:

実際の価値と受け取った価値の違いの原因は何ですか?

真の価値を受け取る方法はありますか?

そうでない場合、私の解決策はこの問題に適していますか?

他にもっと良い解決策はありますか?

PSこの問題がOSに関連しているかどうかわからないため、Windows-Phoneタグを設定しません。分離ストレージツールを使用してダウンロードしたファイルのサイズを確認しましたが、受信した値と同じ値が表示されました(スクリーンショットのロシア語については申し訳ありません)。

ファイルサイズが間違っている画像

4

4 に答える 4

0

私はあなたの更新に答えています:

これはこれまでの私の理解です。ファイルに実際に書き込まれる長さは、実際に書き込まれた長さよりも長くなります(次の1KiBに切り上げられます)。これにより、「file.Length==ダウンロード量」の仮定が正しくなくなります。

1つの解決策は、この情報を個別に追跡することです。ダウンロードされたブロックとファイルの全体のサイズを正確に追跡するために、いくつかのメタデータ構造(同じストレージメカニズムを使用して永続化できます)を作成します。

[DataContract] //< I forgot how serialization on the phone works, please forgive me if the tags differ
struct Metadata
{
     [DataMember]
     public int Length;
     [DataMember]
     public int NumBlocksDownloaded;
}

これは、ダウンロードされたブロックとダウンロードされていないブロックを再構築するのに十分です。ただし、それらを連続してダウンロードし続けると仮定します。

編集

もちろん、データをストリームに書き込む前に、コードを単純な追加からストリームの位置を正しいブロックに移動するように変更する必要があります。

 file.Position = currentBlock * delta;
 file.Write(block, 0, block.Length);
于 2013-02-13T11:59:26.573 に答える
0

私のコメントに続きます。

あなたの説明から私が理解している元のファイルサイズは2432000バイトです。
チャンクサイズは304160バイト(または「デルタ」あたり304160)に設定されます。

したがって、ファイルを送信するマシンは7つのチャンクを埋めて送信することができました。
これで、受信側のマシンは7x304160バイト=2129120バイトになります。

最後のチャンクは、埋めるのに十分なバイトが残っていないため、最後まで埋められません。したがって、次のようになります。2432000-2129120 = 302880(304160未満)

数字を追加すると、7x304160 + 1x302880 = 2432000バイトになります。したがって、元のファイルは完全に宛先に転送されます。

問題は、8x304160 = 2433280を計算していて、最後のチャンクでさえ完全に埋める必要があると主張していることです-しかし、何で?? なぜ??

謙虚に..あなたはある種の数学の混乱に閉じ込められていますか、それとも私はあなたの問題を誤解しましたか?
答えてください、元のファイルサイズともう一方の端で受信されているサイズは何ですか?(合計!)

于 2013-02-14T10:42:00.463 に答える
0

考えられるバグと同じように。リクエスト中にファイルが変更されたかどうかを確認することを忘れないでください。特に、一時停止/再開時に発生する可能性のある、長い時間の間に発生する可能性があります。エラーが大きくなる可能性があります。たとえば、ファイルが小さいサイズに変更され、カウントが「エラー」になり、ファイルが同じサイズであるが内容が変更されている場合、ファイルが破損したままになります。

于 2013-02-18T19:19:52.197 に答える
0

noob-programmerと10人のguru-programmerについての逸話を聞いたことがありますか?達人のプログラマーは彼の解決策の誤りを見つけようとしていて、noobはすでにそれを見つけていましたが、それは愚かなことだったので、私たちは笑われるのを恐れていました。

なぜ私はこれを覚えたのですか?状況が似ているからです。

私の質問の説明は非常に重く、私はいくつかの小さな側面については言及しないことにしました。確かに、正しく機能しました。(そして彼らは本当に正しく働いた)

この小さな側面の1つは、ダウンロードされたファイルがAESPKCS7パディングを介して暗号化されたという事実でした。さて、復号化は正しく機能しました、私はそれを知っていました、それでなぜ私はそれについて言及する必要がありますか?そして、私はしませんでした。

それで、私は最後のチャンクでエラーを正確に引き起こしているものを見つけようとしました。最も信頼できるバージョンはバッファリングの問題に関するもので、不足しているバイトをどこに残しているかを見つけようとしました。何度もテストしましたが、すべてのチャンクが損失なく保存されていたため、それらを見つけることができませんでした。そしてある日、私は理解しました:

スプーンはありません

エラーはありません。

AES PKCS7のポイントは何ですか?さて、主なものは、復号化されたファイルを小さくすることです。それほど多くはありませんが、16バイトだけです。そして、それは私の復号化方法とダウンロード方法で考慮されたので、問題はないはずですよね?

しかし、ダウンロードプロセスが中断するとどうなりますか?最後のチャンクは正しく保存され、バッファリングやその他のエラーは発生しません。そして、ダウンロードを続けたいと思います。ダウンロードされたチャンクの数は次のようになりますcurrentFileChunkIterator = currentFileSize / delta;

そして、ここで私は自分自身に問いかけるべきです:「なぜあなたはそんなに愚かなことをしようとしているのですか?」

「ダウンロードした1つのチャンクサイズはデルタではありません。実際には、デルタよりも小さいです」。(復号化により、チャンクが16バイトに小さくなります。覚えていますか?)

デルタ自体は、復号化されている10個の等しい部分で構成されています。したがって、デルタではなく、(デルタ-16 * 10)で除算する必要があります。これは(304160-160)= 304000です。

ここでネズミを感じます。ダウンロードされたチャンクの数を調べてみましょう。

2432000/304000=8.待って... OHSHI〜


これで話は終わりです。

ソリューションロジック全体が正しかった。

それが失敗した唯一の理由は、何らかの理由で、ダウンロードされた復号化されたファイルのサイズは、ダウンロードされた暗号化されたチャンクの合計と同じである必要があるという私の考えでした。

そしてもちろん、復号化については触れていませんでした(前の質問でのみ言及されており、リンクされているだけです)ので、誰も私に正しい答えを与えることができませんでした。大変申し訳ございません。

于 2013-02-20T12:43:38.120 に答える