@@ -68,8 +68,7 @@ func (f *file) Save(pieces torrent.PieceManager, dst string) error {
6868 return f .saveSingleFile (pieces , dst )
6969 }
7070
71- // TODO: add support for saving multifile torrents
72- return nil
71+ return f .saveMultiFile (pieces , dst )
7372}
7473
7574// saveSingleFile saves a single-file torrent as a file, fetching the pieces
@@ -99,6 +98,90 @@ func (f *file) saveSingleFile(pieces torrent.PieceManager, dst string) error {
9998 return nil
10099}
101100
101+ func (f * file ) saveMultiFile (pieces torrent.PieceManager , dst string ) error {
102+ fileIndex , left := 0 , 0
103+
104+ var file * os.File
105+ defer file .Close ()
106+
107+ var err error
108+
109+ // nextFile closes the current file and opens the next file
110+ nextFile := func () error {
111+ if file != nil {
112+ // close the current file
113+ file .Close ()
114+ }
115+
116+ fileinfo := f .Info .Files [fileIndex ]
117+ filepath := []string {dst }
118+ filepath = append (filepath , fileinfo .Path ... )
119+
120+ // create new file
121+ file , err = os .Create (path .Join (filepath ... ))
122+ if err != nil {
123+ return err
124+ }
125+
126+ fileIndex ++
127+ left = fileinfo .Length
128+ return nil
129+ }
130+
131+ // open the first file
132+ err = nextFile ()
133+ if err != nil {
134+ return err
135+ }
136+
137+ pieceNum := len (f .Info .Pieces ) / 20
138+
139+ pieceLoop:
140+ // loop through all the pieces
141+ for i := 0 ; i < pieceNum ; i ++ {
142+
143+ // get next piece
144+ piece , err := pieces .Get (i )
145+ if err != nil {
146+ return err
147+ }
148+ consumed := 0
149+
150+ // repeat until whole piece is consumed
151+ for {
152+ piece = piece [consumed :]
153+ length := len (piece )
154+
155+ switch {
156+ // current file will consume whole piece
157+ case left >= length :
158+ _ , err := file .Write (piece )
159+ if err != nil {
160+ return err
161+ }
162+
163+ left -= length
164+ continue pieceLoop
165+
166+ // current file finished
167+ case left == 0 :
168+ err := nextFile ()
169+ if err != nil {
170+ return err
171+ }
172+
173+ // current file will consume a part of the piece
174+ case left < length :
175+ file .Write (piece [:left ])
176+ consumed += left
177+ left = 0
178+ }
179+ }
180+ }
181+
182+ return nil
183+ }
184+
102185// Torrent converts a file into a torrent.Torrent.
103186func (f * file ) Torrent () (* torrent.Torrent , error ) {
104187 hash , err := f .Info .hash ()
0 commit comments