Go語言網路爬蟲多重讀取器的實現

2020-07-16 10:04:49
相比前面兩節中介紹的緩衝器和緩衝池,多重讀取器的實現就簡單多了。首先是基本結構:
//多重讀取器的實現型別
type myMultipleReader struct {
    data []byte
}
非常簡單和直接,多重讀取器只儲存要讀取的實際資料。NewMultipleReader 用於新建一個多重讀取器的範例:
//用於新建並返回一個多重讀取器的範例
func NewMultipleReader(reader io.Reader) (MultipleReader, error) {
    var data []byte
    var err error
    if reader != nil {
        data, err = ioutil.ReadAll(reader)
        if err != nil {
            return nil, fmt.Errorf("multipie reader: couldn't create a new one: %s", err)
        }
    } else {
        data = []byte{}
    }
    return &myMultipleReader{
        data: data,
    }, nil
}
標準庫程式碼包 ioutil 中有一些非常實用的函數。這裡用到的 ReadAll 函數的功能是,通過作為引數的讀取器讀取所有底層資料,並忽略 io.EOF 錯誤。

實際上,當碰到 io.EOF 錯誤時,該函數就會返回讀到的所有資料,這正是 data 欄位所代表的資料。另外,*myMultipleReader 應該作為 MultipleReader 介面的實現型別。對於後者宣告的唯一方法,其實現極其簡單:
func (rr *myMultipleReader) Reader() io.ReadCloser {
    return ioutil.NopCloser(bytes.NewReader(rr.data))
}
bytes.NewReader 函數的作用是根據引數生成並返回一個 *bytes.Reader 型別的值。

*bytes.Reader 型別是 io.Reader 介面的一個實現型別,不過我們這裡需要的是 io.ReadCloser 介面型別的值。所以,需要使用 ioutil.NopCloser 函數對這個 *bytes.Reader 型別的值進行簡單的包裝。ioutil.NopCloser 函數會返回一個 io.ReadCloser 型別的值。

之前說過,io.ReadCloser 介面只比 io.Reader 多宣告了一個 Close 方法,這個 Close 方法沒有引數宣告,但有一個 error 型別的結果宣告。然而,ioutil.NopCloser 函數的結果值的 Close 方法永遠只會返回 nil。也正因為如此,我們常常用這個函數包裝無需關閉的讀取器,這就是 NopCloser 的含義。

多重讀取器的 Reader 方法總是返回一個新的可關閉讀取器。因此,我可以利用它多次讀取底層資料,並可以用該方法的結果值替代原先的 HTTP 響應的 Body 欄位值很多次,這也是“多重”的真正含義。