Provide JoinFilesWithPrefix case with Gochannel

This commit is contained in:
Harshavardhana 2014-12-05 09:49:07 -08:00
parent 1b283441a4
commit 9a5003dd25
5 changed files with 104 additions and 18 deletions

View File

@ -48,7 +48,7 @@ func encode(c *cli.Context) {
} }
} else { } else {
chunkCount := 0 chunkCount := 0
splitChannel := make(chan split.ByteMessage) splitChannel := make(chan split.SplitMessage)
inputReader := bytes.NewReader(input) inputReader := bytes.NewReader(input)
go split.SplitStream(inputReader, config.blockSize, splitChannel) go split.SplitStream(inputReader, config.blockSize, splitChannel)
for chunk := range splitChannel { for chunk := range splitChannel {

View File

@ -1,2 +1,2 @@
TESTFILE.*
TESTPREFIX.* TESTPREFIX.*
NEWFILE

View File

@ -8,4 +8,4 @@ test: build
@godep go test -race -coverprofile=cover.out @godep go test -race -coverprofile=cover.out
clean: clean:
@rm -fv TESTFILE.* TESTPREFIX.* cover.out @rm -fv NEWFILE TESTPREFIX.* cover.out

View File

@ -22,25 +22,26 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"errors" "errors"
"github.com/minio-io/minio/pkgs/strbyteconv"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"strconv" "strconv"
"strings"
"github.com/minio-io/minio/pkgs/strbyteconv"
) )
type Split struct {
file string
offset uint64
}
// Message structure for results from the SplitStream goroutine // Message structure for results from the SplitStream goroutine
type ByteMessage struct { type SplitMessage struct {
Data []byte Data []byte
Err error Err error
} }
type JoinMessage struct {
Reader io.Reader
Length int64
Err error
}
// SplitStream reads from io.Reader, splits the data into chunks, and sends // SplitStream reads from io.Reader, splits the data into chunks, and sends
// each chunk to the channel. This method runs until an EOF or error occurs. If // each chunk to the channel. This method runs until an EOF or error occurs. If
// an error occurs, the method sends the error over the channel and returns. // an error occurs, the method sends the error over the channel and returns.
@ -49,12 +50,12 @@ type ByteMessage struct {
// The user should run this as a gorountine and retrieve the data over the // The user should run this as a gorountine and retrieve the data over the
// channel. // channel.
// //
// channel := make(chan ByteMessage) // channel := make(chan SplitMessage)
// go SplitStream(reader, chunkSize, channel) // go SplitStream(reader, chunkSize, channel)
// for chunk := range channel { // for chunk := range channel {
// log.Println(chunk.Data) // log.Println(chunk.Data)
// } // }
func SplitStream(reader io.Reader, chunkSize uint64, ch chan ByteMessage) { func SplitStream(reader io.Reader, chunkSize uint64, ch chan SplitMessage) {
// we read until EOF or another error // we read until EOF or another error
var readError error var readError error
@ -83,22 +84,102 @@ func SplitStream(reader io.Reader, chunkSize uint64, ch chan ByteMessage) {
bytesWriter.Flush() bytesWriter.Flush()
// if we have data available, send it over the channel // if we have data available, send it over the channel
if bytesBuffer.Len() != 0 { if bytesBuffer.Len() != 0 {
ch <- ByteMessage{bytesBuffer.Bytes(), nil} ch <- SplitMessage{bytesBuffer.Bytes(), nil}
} }
} }
// if we have an error other than an EOF, send it over the channel // if we have an error other than an EOF, send it over the channel
if readError != io.EOF { if readError != io.EOF {
ch <- ByteMessage{nil, readError} ch <- SplitMessage{nil, readError}
} }
// close the channel, signaling the channel reader that the stream is complete // close the channel, signaling the channel reader that the stream is complete
close(ch) close(ch)
} }
func JoinStream(dirname string, inputPrefix string, ch chan JoinMessage) {
var readError error
var bytesBuffer bytes.Buffer
bytesWriter := bufio.NewWriter(&bytesBuffer)
// read a full directory
fileInfos, readError := ioutil.ReadDir(dirname)
if readError != nil {
ch <- JoinMessage{nil, 0, readError}
}
var newfileInfos []os.FileInfo
for _, fi := range fileInfos {
if strings.Contains(fi.Name(), inputPrefix) == true {
newfileInfos = append(newfileInfos, fi)
continue
}
}
if len(newfileInfos) == 0 {
ch <- JoinMessage{nil, 0, errors.New("no files found for given prefix")}
}
for i := range newfileInfos {
slice, err := ioutil.ReadFile(newfileInfos[i].Name())
if err != nil {
ch <- JoinMessage{nil, 0, err}
}
bytesWriter.Write(slice)
bytesWriter.Flush()
if bytesBuffer.Len() != 0 {
ch <- JoinMessage{&bytesBuffer, newfileInfos[i].Size(), nil}
}
}
// close the channel, signaling the channel reader that the stream is complete
close(ch)
}
func JoinFilesWithPrefix(dirname string, inputPrefix string, outputFile string) error {
if dirname == "" {
return errors.New("Invalid directory")
}
if inputPrefix == "" {
return errors.New("Invalid argument inputPrefix cannot be empty string")
}
if outputFile == "" {
return errors.New("Invalid output file")
}
ch := make(chan JoinMessage)
go JoinStream(dirname, inputPrefix, ch)
var multiReaders []io.Reader
var aggregatedLength int64
for output := range ch {
if output.Err != nil {
return output.Err
}
multiReaders = append(multiReaders, output.Reader)
aggregatedLength += output.Length
}
newReader := io.MultiReader(multiReaders...)
aggregatedBytes := make([]byte, aggregatedLength)
_, err := newReader.Read(aggregatedBytes)
if err != nil {
return err
}
err = ioutil.WriteFile(outputFile, aggregatedBytes, 0600)
if err != nil {
return err
}
return nil
}
// Takes a file and splits it into chunks with size chunkSize. The output // Takes a file and splits it into chunks with size chunkSize. The output
// filename is given with outputPrefix. // filename is given with outputPrefix.
func SplitFilesWithPrefix(filename string, chunkstr string, outputPrefix string) error { func SplitFilesWithPrefix(filename string, chunkstr string, outputPrefix string) error {
// open file // open file
file, err := os.Open(filename) file, err := os.Open(filename)
defer file.Close()
if err != nil { if err != nil {
return err return err
} }
@ -113,7 +194,7 @@ func SplitFilesWithPrefix(filename string, chunkstr string, outputPrefix string)
} }
// start stream splitting goroutine // start stream splitting goroutine
ch := make(chan ByteMessage) ch := make(chan SplitMessage)
go SplitStream(file, chunkSize, ch) go SplitStream(file, chunkSize, ch)
// used to write each chunk out as a separate file. {{outputPrefix}}.{{i}} // used to write each chunk out as a separate file. {{outputPrefix}}.{{i}}

View File

@ -40,7 +40,7 @@ func (s *MySuite) TestSplitStream(c *C) {
} }
bytesWriter.Flush() bytesWriter.Flush()
log.Println(strconv.Itoa(bytesBuffer.Len())) log.Println(strconv.Itoa(bytesBuffer.Len()))
ch := make(chan ByteMessage) ch := make(chan SplitMessage)
reader := bytes.NewReader(bytesBuffer.Bytes()) reader := bytes.NewReader(bytesBuffer.Bytes())
go SplitStream(reader, 25, ch) go SplitStream(reader, 25, ch)
var resultsBuffer bytes.Buffer var resultsBuffer bytes.Buffer
@ -52,9 +52,14 @@ func (s *MySuite) TestSplitStream(c *C) {
c.Assert(bytes.Compare(bytesBuffer.Bytes(), resultsBuffer.Bytes()), Equals, 0) c.Assert(bytes.Compare(bytesBuffer.Bytes(), resultsBuffer.Bytes()), Equals, 0)
} }
func (s *MySuite) TestFileSplit2(c *C) { func (s *MySuite) TestFileSplitJoin(c *C) {
err := SplitFilesWithPrefix("TESTFILE", "1KB", "TESTPREFIX") err := SplitFilesWithPrefix("TESTFILE", "1KB", "TESTPREFIX")
c.Assert(err, IsNil) c.Assert(err, IsNil)
err = SplitFilesWithPrefix("TESTFILE", "1KB", "") err = SplitFilesWithPrefix("TESTFILE", "1KB", "")
c.Assert(err, Not(IsNil)) c.Assert(err, Not(IsNil))
err = JoinFilesWithPrefix(".", "TESTPREFIX", "")
c.Assert(err, Not(IsNil))
err = JoinFilesWithPrefix(".", "TESTPREFIX", "NEWFILE")
c.Assert(err, IsNil)
} }