<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 類比較簡單,我直接在源碼基礎上進行注釋。掌握 Storage ,為進一步分析 StorageWrapper 類打下基礎。

     

    幾點說明:

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

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

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

    4、  由于下載可能中斷,所以在 Storage 初始化的時候,磁盤上可能已經存在文件的部分數據,必須檢查一下文件的大小。為了便于描述,我們把完整文件的大小稱為“實際長度”,把文件當前的大小成為“當前長度”。

     

     

    class Storage:

     

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

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

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

     

            self.ranges = []

     

    # 注意,這里是 0l ,后面的 l 表示類型是長整形,而不是 01

            total = 0l

            so_far = 0l

     

            for file, length in files:

                if length != 0:

       

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

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

                    total += length

                   

                   # so_far 是實際存在的文件的總長度,好像沒有起作用

                    if exists(file):

                        l = getsize(file)

                        if l > length:

                            l = length

    so_far += l

     

    # 如果文件長度為 0 則創建一個空文件

                   elif not exists(file):

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

       

             # begins 是一個列表,用來保存每個文件的起始位置

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

            self.total_length = total

            self.handles = {}

            self.whandles = {}

            self.tops = {}

     

            # 對于每一個文件,,,

            for file, length in files:

     

                # 如果文件已經存在

                if exists(file):

                    l = getsize(file)

     

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

                    if l != length:

     

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

                   whandles 是一個字典,用來記錄對應文件是否是以寫的方式打開(讀寫也是一種寫)。

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

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

     

    # 如果文件長度大于實際長度,那么應該是出錯了,截斷它。

                        if l > length:

                            self.handles[file].truncate(length)

     

                     如果文件長度和實際長度一致,那么下載已經完成,以只讀方式打開。

                    else:

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

     

     

                   # tops 是一個 字典,保存對應文件的“當前長度”。

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

     

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

                else:

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

                    self.whandles[file] = 1

     

    # 判斷起始位置為 pos ,長度為 length 的文件片斷,在 Storage 初始化之前,是否就已經存在于磁盤上了。這個函數后面分析 StoageWrapper 類的時候會再提到。

    如果已經存在,那么返回 true ,否則為 false

    注意:如果這個片斷的部分數據已經存在于磁盤上的話,那么也返回 false

     

    在分析 StorageWrapper 的時候,才發現這里分析的不對。這個函數意思應該是:

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

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

        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

     

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

        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')

     

    # 獲取所有文件的總長度

        def get_total_length (self):

            return self.total_length

     

     

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

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

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

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

     

        def _intervals (self, pos, amount):

            r = []

     

                  # stop 是這個片斷的結束位置。

            stop = pos + amount

     

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

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

     

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

     

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

           

    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 長的數據從文件中讀出來,轉換成一個字符串

    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 轉換為一個字符串

            return ''.join(r)

     

    # 把一段字符串寫到相應的磁盤文件中。

        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]

     

    # 通過 seek 函數移動文件指針,可以看出來,文件不是按照順序來寫的,因為所獲取的文件片斷是隨機的,所以寫也是隨機的。

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

                h.seek(begin)

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

                total += end - begin

     

    # 關閉所有打開文件

    def close (self):

            for h in self.handles.s():

                h.close()
    posted on 2007-01-19 00:20 苦笑枯 閱讀(389) 評論(0)  編輯  收藏 所屬分類: P2P
    收藏來自互聯網,僅供學習。若有侵權,請與我聯系!

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

    常用鏈接

    留言簿(2)

    隨筆分類(56)

    隨筆檔案(56)

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲人成无码网站在线观看| 亚洲国产第一页www| 亚洲Av高清一区二区三区| 精品在线免费观看| 亚洲乱码无码永久不卡在线| 2022国内精品免费福利视频 | 亚洲欧洲日本天天堂在线观看| 三级黄色免费观看| 亚洲阿v天堂在线| 叮咚影视在线观看免费完整版| 日本亚洲欧洲免费天堂午夜看片女人员| 一级毛片在线播放免费| 久久亚洲av无码精品浪潮| 国产色无码精品视频免费| 国产成人亚洲综合无码精品| 日本人成在线视频免费播放| 亚洲最新永久在线观看| 2015日韩永久免费视频播放 | 久久久久亚洲av无码尤物| 五月亭亭免费高清在线| 亚洲性一级理论片在线观看| 九九九精品成人免费视频| 亚洲欧美国产国产综合一区 | 成人毛片免费视频| 青青免费在线视频| 精品国产亚洲一区二区三区| 91免费在线播放| 亚洲AV无码一区二区三区网址| 亚洲不卡无码av中文字幕| 日本免费电影一区二区| 亚洲喷奶水中文字幕电影| 国产精品酒店视频免费看| 久久久久女教师免费一区| 内射少妇36P亚洲区| 天天摸夜夜摸成人免费视频 | 久久久久亚洲AV无码永不| 国产大片线上免费观看| 在线观看亚洲专区| 亚洲人成网址在线观看| 韩国欧洲一级毛片免费| 97在线免费观看视频|