2017-02-18 7 views
0

Я написал небольшой сервер, который получает блок данных в виде io.Reader, добавляет заголовок и передает результат обратно вызывающему.Добавить префикс в io.Reader

Моя реализация не особенно эффективна, так как я буферизую данные blob в памяти, чтобы вычислить длину блоба, которая должна быть частью заголовка.

Я видел несколько примеров io.Pipe() с io.TeeReader, но они больше подходят для разделения io.Reader на две части и записи их параллельно.

Капли я имею дело с около 100KB, поэтому не огромный, но если мой сервер получает занят, память собирается быстро стать проблемой ...

Любые идеи?

func addHeader(in io.Reader) (out io.Reader, err error) { 
    buf := new(bytes.Buffer) 
    if _, err = io.Copy(buf, in); err != nil { 
     return 
    } 

    header := bytes.NewReader([]byte(fmt.Sprintf("header:%d", buf.Len()))) 

    return io.MultiReader(header, buf), nil 
} 

Я ценю это не очень хорошая идея, чтобы вернуть интерфейсы из функций, но этот код не суждено стать API, так что я не слишком обеспокоен, что немного.

ответ

0

В общем, единственный способ определить длину данных в io.Reader - читать до EOF. Существуют способы определения длины данных для определенных типов.

func addHeader(in io.Reader) (out io.Reader, err error) { 
    n := 0 
    switch v := in.(type) { 
    case *bytes.Buffer: 
    n = v.Len() 
    case *bytes.Reader: 
    n = v.Len() 
    case *strings.Reader: 
    n = v.Len() 
    case io.Seeker: 
    cur, err := v.Seek(0, 1) 
    if err != nil { 
     return nil, err 
    } 
    end, err := v.Seek(0, 2) 
    if err != nil { 
     return nil, err 
    } 
    _, err = v.Seek(cur, 0) 
    if err != nil { 
     return nil, err 
    } 
    n = int(end - cur) 
    default: 
    var buf bytes.Buffer 
    if _, err := buf.ReadFrom(in); err != nil { 
     return nil, err 
    } 
    n = buf.Len() 
    in = &buf 
    } 
    header := strings.NewReader(fmt.Sprintf("header:%d", n)) 
    return io.MultiReader(header, in), nil 
} 

Это подобно тому, как сеть/HTTP пакет determines the content length of the request body.

+0

Спасибо, что ответ, Cerise. Это io.ReadCloser под обложками, поэтому я думаю, мне просто нужно будет его буферизировать. Ура! – Rob

+0

@Rob io.ReadCloser - это тип интерфейса. Конкретный тип - это нечто другое. Попробуйте 'fmt.Println («% T », in)', чтобы увидеть, что такое конкретный тип. Возможно, это будет один из типов, перечисленных в моем ответе, или тот, который вы можете извлечь из него без чтения. –

+0

А, да, я проверю базовую реализацию и посмотрю, смогу ли я извлечь длину. Огромное спасибо! – Rob

Смежные вопросы