<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 56,  comments - 12,  trackbacks - 0

    作者:小馬哥

    日期: 2004-6-28

     

    由于 Storage 類比較簡(jiǎn)單,我直接在源碼基礎(chǔ)上進(jìn)行注釋。掌握 Storage ,為進(jìn)一步分析 StorageWrapper 類打下基礎(chǔ)。

     

    幾點(diǎn)說(shuō)明:

    1、  Storage 類封裝了對(duì)磁盤文件的讀和寫的操作。

    2、  BT 既支持單個(gè)文件的下載,也支持多個(gè)文件,包括可以有子目錄。但是它并不是以文件為單位進(jìn)行下載和上傳的,而是以“文件片斷”為單位。這可以在 BT 協(xié)議規(guī)范以及另一篇講 BT 技術(shù)的文章中看到。所以,對(duì)于多個(gè)文件的情況,它也是當(dāng)作一個(gè)拼接起來(lái)的“大文件”來(lái)處理的。例如,有文件 aaa bbb ,大小分別是 400 1000 ,那么它看作一個(gè)大小為 1400 的大文件,并以此來(lái)進(jìn)行片斷劃分。

    3、  文件在下載過(guò)程中,同時(shí)提供上傳,所以是以讀寫方式打開的, wb+ rb+ 都指的讀寫方式。在下載完畢之后,改為只讀方式。

    4、  由于下載可能中斷,所以在 Storage 初始化的時(shí)候,磁盤上可能已經(jīng)存在文件的部分?jǐn)?shù)據(jù),必須檢查一下文件的大小。為了便于描述,我們把完整文件的大小稱為“實(shí)際長(zhǎng)度”,把文件當(dāng)前的大小成為“當(dāng)前長(zhǎng)度”。

     

     

    class Storage:

     

    # files 是一個(gè)二元組的列表( list ),二元組包含了文件名稱和長(zhǎng)度,例如:

    [(“aaa”, 100), (“bbb”, 200)]

    def __init_ _(self, files, open, exists, getsize):

     

            self.ranges = []

     

    # 注意,這里是 0l ,后面的 l 表示類型是長(zhǎng)整形,而不是 01 。

            total = 0l

            so_far = 0l

     

            for file, length in files:

                if length != 0:

       

                   # ranges 是一個(gè)三元組列表,三元組的格式是: 在“整個(gè)”文件的起始位置、結(jié)束位置、文件名。 BT 在處理多個(gè)文件的時(shí)候,是把它們看作一個(gè)拼接起來(lái)的大文件。

                    self.ranges.append((total, total + length, file))

                    total += length

                   

                   # so_far 是實(shí)際存在的文件的總長(zhǎng)度,好像沒有起作用

                    if exists(file):

                        l = getsize(file)

                        if l > length:

                            l = length

    so_far += l

     

    # 如果文件長(zhǎng)度為 0 則創(chuàng)建一個(gè)空文件

                   elif not exists(file):

                    open(file, 'wb').close()

       

             # begins 是一個(gè)列表,用來(lái)保存每個(gè)文件的起始位置

            self.begins = [i[0] for i in self.ranges]

            self.total_length = total

            self.handles = {}

            self.whandles = {}

            self.tops = {}

     

            # 對(duì)于每一個(gè)文件,,,

            for file, length in files:

     

                # 如果文件已經(jīng)存在

                if exists(file):

                    l = getsize(file)

     

                   # 如果文件長(zhǎng)度不一致,說(shuō)明還沒有下載完全,則以讀寫( rb+ )的方式打開文件。

                    if l != length:

     

                   handles 是一個(gè)字典,用來(lái)保存所有被打開文件(無(wú)論是只讀還是讀寫)的句柄

                   whandles 是一個(gè)字典,用來(lái)記錄對(duì)應(yīng)文件是否是以寫的方式打開(讀寫也是一種寫)。

                        self.handles[file] = open(file, 'rb+')

    self.whandles[file] = 1 (這里是數(shù)字 1 ,而不是字母 l

     

    # 如果文件長(zhǎng)度大于實(shí)際長(zhǎng)度,那么應(yīng)該是出錯(cuò)了,截?cái)嗨?/span>

                        if l > length:

                            self.handles[file].truncate(length)

     

                     如果文件長(zhǎng)度和實(shí)際長(zhǎng)度一致,那么下載已經(jīng)完成,以只讀方式打開。

                    else:

    self.handles[file] = open(file, 'rb')

     

     

                   # tops 是一個(gè) 字典,保存對(duì)應(yīng)文件的“當(dāng)前長(zhǎng)度”。

                    self.tops[file] = l (這里是字母 l ,不是數(shù)字 1

     

                # 如果文件并不存在,那么以讀寫( w+ )的方式打開

                else:

                    self.handles[file] = open(file, 'wb+')

                    self.whandles[file] = 1

     

    # 判斷起始位置為 pos ,長(zhǎng)度為 length 的文件片斷,在 Storage 初始化之前,是否就已經(jīng)存在于磁盤上了。這個(gè)函數(shù)后面分析 StoageWrapper 類的時(shí)候會(huì)再提到。

    如果已經(jīng)存在,那么返回 true ,否則為 false 。

    注意:如果這個(gè)片斷的部分?jǐn)?shù)據(jù)已經(jīng)存在于磁盤上的話,那么也返回 false 。

     

    在分析 StorageWrapper 的時(shí)候,才發(fā)現(xiàn)這里分析的不對(duì)。這個(gè)函數(shù)意思應(yīng)該是:

    判斷起始位置為 pos ,長(zhǎng)度為 length 的文件片斷,在 Storage 初始化之前,是否已經(jīng)在磁盤上 分配 了空間。

    例如,大小為 1024k 的文件,如果獲得了 1 個(gè)片斷(從 256k 512k ),那么這時(shí)候,磁盤上文件的大小是 512k (也就是分配了 512k ),盡管第 0 個(gè)片斷(從 0 256k )還沒有獲得,但磁盤上會(huì)保留這個(gè)“空洞”。

        def was_preallocated (self, pos, length):

             for file, begin, end in self._intervals(pos, length):

                if self.tops.get(file, 0) < end:

                    return False

            return True

     

    # 將所有原來(lái)以 讀寫方式打開的文件,改成只讀方式打開

        def set_readonly (self):

            # may raise IOError or OSError

             for file in self.whandles.keys():

                old = self.handles[file]

                old.flush()

                old.close()

                self.handles[file] = open(file, 'rb')

     

    # 獲取所有文件的總長(zhǎng)度

        def get_total_length (self):

            return self.total_length

     

     

    這個(gè)函數(shù)意思是檢查 起始位置 pos ,大小為 amount 的片斷實(shí)際位置在哪里?

    例如,假設(shè)有兩個(gè)文件, aaa bbb ,大小分別是 400 1000 ,那么 pos 300 , amount 200 的文件片斷屬于哪個(gè)文件了?它分別屬于兩個(gè)文件,所以返回的是

    [(“aaa”, 300, 400), (“bbb”, 0, 100)] ,

    也就是它既包含了 aaa 文件中從 300 400 這段數(shù)據(jù),也包含了 bbb 文件從 0 100 這段數(shù)據(jù)。

     

        def _intervals (self, pos, amount):

            r = []

     

                  # stop 是這個(gè)片斷的結(jié)束位置。

            stop = pos + amount

     

    # 通過(guò)這個(gè)函數(shù),可以首先定位在哪個(gè)文件中,注意,可能在多個(gè)文件中(如果某個(gè)文件過(guò)小,那么,一段數(shù)據(jù)可能跨越幾個(gè)文件)

    # 通過(guò)例子來(lái)解釋下面這句,假設(shè) begins = [ 100, 200, 400, 1000], pos = 250 ,那么 bisect_right(self.begins, pos) 返回的是 2 ,而 p = bisect_right(self.begins, pos) – 1 就是 1 ,這表示起始位置為 250 的文件“片斷”,它至少屬于第 1 個(gè)文件(從 0 開始算起),也就是起始為 200 的文件。

     

    p = bisect_right(self.begins, pos) – 1

     

    # r 是一個(gè)三元組的列表,三元組格式是(文件名,在該文件的起始位置,在該文件的結(jié)束位置)。

           

    while p < len(self.ranges) and self.ranges[p][0] < stop:

                begin, end, file = self.ranges[p]

                r.append((file, max(pos, begin) - begin, min(end, stop) - begin))

                p += 1

     

            return r

          

    # 把從 pos 開始, amount 長(zhǎng)的數(shù)據(jù)從文件中讀出來(lái),轉(zhuǎn)換成一個(gè)字符串

    def read (self, pos, amount):

            r = []

             for file, pos, end in self._intervals(pos, amount):

                h = self.handles[file]

                h.seek(pos)

    r.append(h.read(end - pos))

     

            # list 轉(zhuǎn)換為一個(gè)字符串

            return ''.join(r)

     

    # 把一段字符串寫到相應(yīng)的磁盤文件中。

        def write (self, pos, s):

            # might raise an IOError

            total = 0

            for file, begin, end in self._intervals(pos, len(s)):

     

                # 如果該文件并不是以寫的方式打開的,那么改成讀寫的方式打開

                if not self.whandles.has_key(file):

                    self.handles[file].close()

                    self.handles[file] = open(file, 'rb+')

                    self.whandles[file] = 1

    h = self.handles[file]

     

    # 通過(guò) seek 函數(shù)移動(dòng)文件指針,可以看出來(lái),文件不是按照順序來(lái)寫的,因?yàn)樗@取的文件片斷是隨機(jī)的,所以寫也是隨機(jī)的。

    # 這里有一個(gè)疑問,假設(shè)獲得了第二個(gè)文件片斷,起始是 1000 ,大小是 500 ,而第一個(gè)片斷還沒有獲得,那么文件指針要移動(dòng)到 1000 處,并寫 500 個(gè)字節(jié)。這時(shí)候,文件的大小應(yīng)該是 1500 ,盡管前面 1000 個(gè)字節(jié)是“空洞”。那么如果,直到結(jié)束,都沒有獲得第一個(gè)片斷,又如何檢測(cè)出來(lái)了?(通過(guò)檢查 total ?)

                h.seek(begin)

                h.write(s[total: total + end - begin])

                total += end - begin

     

    # 關(guān)閉所有打開文件

    def close (self):

            for h in self.handles.s():

                h.close()
    posted on 2007-01-19 00:20 苦笑枯 閱讀(394) 評(píng)論(0)  編輯  收藏 所屬分類: P2P
    收藏來(lái)自互聯(lián)網(wǎng),僅供學(xué)習(xí)。若有侵權(quán),請(qǐng)與我聯(lián)系!

    <2007年1月>
    31123456
    78910111213
    14151617181920
    21222324252627
    28293031123
    45678910

    常用鏈接

    留言簿(2)

    隨筆分類(56)

    隨筆檔案(56)

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    主站蜘蛛池模板: 亚洲日韩国产一区二区三区在线| 久久亚洲色一区二区三区| 思思re热免费精品视频66 | 亚洲三级在线视频| 亚洲综合自拍成人| 久久精品国产精品亚洲毛片| 午夜亚洲国产理论秋霞| 久久综合图区亚洲综合图区| 日本亚洲视频在线| 亚洲AV无码一区二区乱子伦| 亚洲国产日韩在线视频| 亚洲色欲色欲www在线丝| 国产日韩亚洲大尺度高清| 亚洲av综合av一区| 亚洲今日精彩视频| 亚洲另类春色国产精品| 亚洲欧美乱色情图片| 男女超爽视频免费播放| www在线观看免费视频| 日韩av无码免费播放| 少妇人妻偷人精品免费视频| 1000部禁片黄的免费看| 在线a级毛片免费视频| 日本免费网站在线观看| 亚洲精品人成无码中文毛片| 亚洲自偷自偷在线制服| 久久精品国产亚洲AV网站| 亚洲欧洲日韩在线电影| 亚洲人成人伊人成综合网无码| 精品国产日韩亚洲一区91| 一级毛片免费全部播放| 免费国产叼嘿视频大全网站| 日本最新免费网站| 国产亚洲精品免费| 亚洲精品无码久久一线| 亚洲国产精品午夜电影| 国产精品久久久久久亚洲影视| 男女一进一出抽搐免费视频 | 国产午夜亚洲精品不卡免下载 | 亚洲人成色99999在线观看| 国产一区二区三区亚洲综合|