this runs asynchronously and uses FileLockMgr
(id string, compress string, numParts int, allowEmpty bool)
| 218 | |
| 219 | // this runs asynchronously and uses FileLockMgr |
| 220 | func SetFileFromParts(id string, compress string, numParts int, allowEmpty bool) { |
| 221 | // use function closure to get current value of err at return time |
| 222 | var err error |
| 223 | defer func() { |
| 224 | locker.FileLockMgr.Error(id, err) |
| 225 | }() |
| 226 | |
| 227 | outf := fmt.Sprintf("%s/%s.data", getPath(id), id) |
| 228 | |
| 229 | var outh *os.File |
| 230 | outh, err = os.Create(outf) |
| 231 | if err != nil { |
| 232 | return |
| 233 | } |
| 234 | defer outh.Close() |
| 235 | |
| 236 | pReader, pWriter := io.Pipe() |
| 237 | defer pReader.Close() |
| 238 | |
| 239 | cError := make(chan error) |
| 240 | cKill := make(chan bool) |
| 241 | |
| 242 | // goroutine to add file readers to pipe while reading pipe |
| 243 | // allows us to only open one file at a time but stream the data as if it was one reader |
| 244 | go func() { |
| 245 | var ferr error |
| 246 | killed := false |
| 247 | for i := 1; i <= numParts; i++ { |
| 248 | select { |
| 249 | case <-cKill: |
| 250 | killed = true |
| 251 | break |
| 252 | default: |
| 253 | } |
| 254 | filename := fmt.Sprintf("%s/parts/%d", getPath(id), i) |
| 255 | // skip this portion unless either |
| 256 | // 1. file exists, or |
| 257 | // 2. file does not exist and allowEmpty == false |
| 258 | if _, errf := os.Stat(filename); errf == nil || (errf != nil && allowEmpty == false) { |
| 259 | var part *os.File |
| 260 | part, ferr = os.Open(filename) |
| 261 | if ferr != nil { |
| 262 | break |
| 263 | } |
| 264 | _, ferr = io.Copy(pWriter, part) |
| 265 | part.Close() |
| 266 | if ferr != nil { |
| 267 | break |
| 268 | } |
| 269 | } |
| 270 | } |
| 271 | pWriter.Close() |
| 272 | select { |
| 273 | case <-cKill: |
| 274 | killed = true |
| 275 | default: |
| 276 | } |
| 277 | if killed { |