You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

667 lines
20 KiB

5 years ago
4 years ago
7 years ago
4 years ago
5 years ago
4 years ago
4 years ago
3 years ago
5 years ago
3 years ago
5 years ago
5 years ago
3 years ago
  1. package command
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/chrislusf/seaweedfs/weed/filer"
  6. "io"
  7. "io/ioutil"
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "path/filepath"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "time"
  16. "google.golang.org/grpc"
  17. "github.com/chrislusf/seaweedfs/weed/util/grace"
  18. "github.com/chrislusf/seaweedfs/weed/operation"
  19. "github.com/chrislusf/seaweedfs/weed/pb"
  20. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  21. "github.com/chrislusf/seaweedfs/weed/security"
  22. "github.com/chrislusf/seaweedfs/weed/storage/needle"
  23. "github.com/chrislusf/seaweedfs/weed/util"
  24. "github.com/chrislusf/seaweedfs/weed/wdclient"
  25. )
  26. var (
  27. copy CopyOptions
  28. waitGroup sync.WaitGroup
  29. )
  30. type CopyOptions struct {
  31. include *string
  32. replication *string
  33. collection *string
  34. ttl *string
  35. diskType *string
  36. maxMB *int
  37. masterClient *wdclient.MasterClient
  38. concurrenctFiles *int
  39. concurrenctChunks *int
  40. grpcDialOption grpc.DialOption
  41. masters []string
  42. cipher bool
  43. ttlSec int32
  44. checkSize *bool
  45. verbose *bool
  46. }
  47. func init() {
  48. cmdFilerCopy.Run = runCopy // break init cycle
  49. cmdFilerCopy.IsDebug = cmdFilerCopy.Flag.Bool("debug", false, "verbose debug information")
  50. copy.include = cmdFilerCopy.Flag.String("include", "", "pattens of files to copy, e.g., *.pdf, *.html, ab?d.txt, works together with -dir")
  51. copy.replication = cmdFilerCopy.Flag.String("replication", "", "replication type")
  52. copy.collection = cmdFilerCopy.Flag.String("collection", "", "optional collection name")
  53. copy.ttl = cmdFilerCopy.Flag.String("ttl", "", "time to live, e.g.: 1m, 1h, 1d, 1M, 1y")
  54. copy.diskType = cmdFilerCopy.Flag.String("disk", "", "[hdd|ssd|<tag>] hard drive or solid state drive or any tag")
  55. copy.maxMB = cmdFilerCopy.Flag.Int("maxMB", 4, "split files larger than the limit")
  56. copy.concurrenctFiles = cmdFilerCopy.Flag.Int("c", 8, "concurrent file copy goroutines")
  57. copy.concurrenctChunks = cmdFilerCopy.Flag.Int("concurrentChunks", 8, "concurrent chunk copy goroutines for each file")
  58. copy.checkSize = cmdFilerCopy.Flag.Bool("check.size", false, "copy when the target file size is different from the source file")
  59. copy.verbose = cmdFilerCopy.Flag.Bool("verbose", false, "print out details during copying")
  60. }
  61. var cmdFilerCopy = &Command{
  62. UsageLine: "filer.copy file_or_dir1 [file_or_dir2 file_or_dir3] http://localhost:8888/path/to/a/folder/",
  63. Short: "copy one or a list of files to a filer folder",
  64. Long: `copy one or a list of files, or batch copy one whole folder recursively, to a filer folder
  65. It can copy one or a list of files or folders.
  66. If copying a whole folder recursively:
  67. All files under the folder and subfolders will be copyed.
  68. Optional parameter "-include" allows you to specify the file name patterns.
  69. If "maxMB" is set to a positive number, files larger than it would be split into chunks.
  70. `,
  71. }
  72. func runCopy(cmd *Command, args []string) bool {
  73. util.LoadConfiguration("security", false)
  74. if len(args) <= 1 {
  75. return false
  76. }
  77. filerDestination := args[len(args)-1]
  78. fileOrDirs := args[0 : len(args)-1]
  79. filerUrl, err := url.Parse(filerDestination)
  80. if err != nil {
  81. fmt.Printf("The last argument should be a URL on filer: %v\n", err)
  82. return false
  83. }
  84. urlPath := filerUrl.Path
  85. if !strings.HasSuffix(urlPath, "/") {
  86. fmt.Printf("The last argument should be a folder and end with \"/\"\n")
  87. return false
  88. }
  89. if filerUrl.Port() == "" {
  90. fmt.Printf("The filer port should be specified.\n")
  91. return false
  92. }
  93. filerPort, parseErr := strconv.ParseUint(filerUrl.Port(), 10, 64)
  94. if parseErr != nil {
  95. fmt.Printf("The filer port parse error: %v\n", parseErr)
  96. return false
  97. }
  98. filerGrpcPort := filerPort + 10000
  99. filerGrpcAddress := fmt.Sprintf("%s:%d", filerUrl.Hostname(), filerGrpcPort)
  100. copy.grpcDialOption = security.LoadClientTLS(util.GetViper(), "grpc.client")
  101. masters, collection, replication, dirBuckets, maxMB, cipher, err := readFilerConfiguration(copy.grpcDialOption, filerGrpcAddress)
  102. if err != nil {
  103. fmt.Printf("read from filer %s: %v\n", filerGrpcAddress, err)
  104. return false
  105. }
  106. if strings.HasPrefix(urlPath, dirBuckets+"/") {
  107. restPath := urlPath[len(dirBuckets)+1:]
  108. if strings.Index(restPath, "/") > 0 {
  109. expectedBucket := restPath[:strings.Index(restPath, "/")]
  110. if *copy.collection == "" {
  111. *copy.collection = expectedBucket
  112. } else if *copy.collection != expectedBucket {
  113. fmt.Printf("destination %s uses collection \"%s\": unexpected collection \"%v\"\n", urlPath, expectedBucket, *copy.collection)
  114. return true
  115. }
  116. }
  117. }
  118. if *copy.collection == "" {
  119. *copy.collection = collection
  120. }
  121. if *copy.replication == "" {
  122. *copy.replication = replication
  123. }
  124. if *copy.maxMB == 0 {
  125. *copy.maxMB = int(maxMB)
  126. }
  127. copy.masters = masters
  128. copy.cipher = cipher
  129. ttl, err := needle.ReadTTL(*copy.ttl)
  130. if err != nil {
  131. fmt.Printf("parsing ttl %s: %v\n", *copy.ttl, err)
  132. return false
  133. }
  134. copy.ttlSec = int32(ttl.Minutes()) * 60
  135. if *cmdFilerCopy.IsDebug {
  136. grace.SetupProfiling("filer.copy.cpu.pprof", "filer.copy.mem.pprof")
  137. }
  138. fileCopyTaskChan := make(chan FileCopyTask, *copy.concurrenctFiles)
  139. go func() {
  140. defer close(fileCopyTaskChan)
  141. for _, fileOrDir := range fileOrDirs {
  142. if err := genFileCopyTask(fileOrDir, urlPath, fileCopyTaskChan); err != nil {
  143. fmt.Fprintf(os.Stderr, "genFileCopyTask : %v\n", err)
  144. break
  145. }
  146. }
  147. }()
  148. for i := 0; i < *copy.concurrenctFiles; i++ {
  149. waitGroup.Add(1)
  150. go func() {
  151. defer waitGroup.Done()
  152. worker := FileCopyWorker{
  153. options: &copy,
  154. filerHost: filerUrl.Host,
  155. filerGrpcAddress: filerGrpcAddress,
  156. }
  157. if err := worker.copyFiles(fileCopyTaskChan); err != nil {
  158. fmt.Fprintf(os.Stderr, "copy file error: %v\n", err)
  159. return
  160. }
  161. }()
  162. }
  163. waitGroup.Wait()
  164. return true
  165. }
  166. func readFilerConfiguration(grpcDialOption grpc.DialOption, filerGrpcAddress string) (masters []string, collection, replication string, dirBuckets string, maxMB uint32, cipher bool, err error) {
  167. err = pb.WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  168. resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
  169. if err != nil {
  170. return fmt.Errorf("get filer %s configuration: %v", filerGrpcAddress, err)
  171. }
  172. masters, collection, replication, maxMB = resp.Masters, resp.Collection, resp.Replication, resp.MaxMb
  173. dirBuckets = resp.DirBuckets
  174. cipher = resp.Cipher
  175. return nil
  176. })
  177. return
  178. }
  179. func genFileCopyTask(fileOrDir string, destPath string, fileCopyTaskChan chan FileCopyTask) error {
  180. fi, err := os.Stat(fileOrDir)
  181. if err != nil {
  182. fmt.Fprintf(os.Stderr, "Error: read file %s: %v\n", fileOrDir, err)
  183. return nil
  184. }
  185. mode := fi.Mode()
  186. uid, gid := util.GetFileUidGid(fi)
  187. fileSize := fi.Size()
  188. if mode.IsDir() {
  189. fileSize = 0
  190. }
  191. fileCopyTaskChan <- FileCopyTask{
  192. sourceLocation: fileOrDir,
  193. destinationUrlPath: destPath,
  194. fileSize: fileSize,
  195. fileMode: fi.Mode(),
  196. uid: uid,
  197. gid: gid,
  198. }
  199. if mode.IsDir() {
  200. files, _ := ioutil.ReadDir(fileOrDir)
  201. for _, subFileOrDir := range files {
  202. cleanedDestDirectory := filepath.Clean(destPath + fi.Name())
  203. if err = genFileCopyTask(fileOrDir+"/"+subFileOrDir.Name(), cleanedDestDirectory+"/", fileCopyTaskChan); err != nil {
  204. return err
  205. }
  206. }
  207. }
  208. return nil
  209. }
  210. type FileCopyWorker struct {
  211. options *CopyOptions
  212. filerHost string
  213. filerGrpcAddress string
  214. }
  215. func (worker *FileCopyWorker) copyFiles(fileCopyTaskChan chan FileCopyTask) error {
  216. for task := range fileCopyTaskChan {
  217. if err := worker.doEachCopy(task); err != nil {
  218. return err
  219. }
  220. }
  221. return nil
  222. }
  223. type FileCopyTask struct {
  224. sourceLocation string
  225. destinationUrlPath string
  226. fileSize int64
  227. fileMode os.FileMode
  228. uid uint32
  229. gid uint32
  230. }
  231. func (worker *FileCopyWorker) doEachCopy(task FileCopyTask) error {
  232. f, err := os.Open(task.sourceLocation)
  233. if err != nil {
  234. fmt.Printf("Failed to open file %s: %v\n", task.sourceLocation, err)
  235. if _, ok := err.(*os.PathError); ok {
  236. fmt.Printf("skipping %s\n", task.sourceLocation)
  237. return nil
  238. }
  239. return err
  240. }
  241. defer f.Close()
  242. // this is a regular file
  243. if *worker.options.include != "" {
  244. if ok, _ := filepath.Match(*worker.options.include, filepath.Base(task.sourceLocation)); !ok {
  245. return nil
  246. }
  247. }
  248. if shouldCopy, err := worker.checkExistingFileFirst(task, f); err != nil {
  249. return fmt.Errorf("check existing file: %v", err)
  250. } else if !shouldCopy {
  251. if *worker.options.verbose {
  252. fmt.Printf("skipping copied file: %v\n", f.Name())
  253. }
  254. return nil
  255. }
  256. // find the chunk count
  257. chunkSize := int64(*worker.options.maxMB * 1024 * 1024)
  258. chunkCount := 1
  259. if chunkSize > 0 && task.fileSize > chunkSize {
  260. chunkCount = int(task.fileSize/chunkSize) + 1
  261. }
  262. if chunkCount == 1 {
  263. return worker.uploadFileAsOne(task, f)
  264. }
  265. return worker.uploadFileInChunks(task, f, chunkCount, chunkSize)
  266. }
  267. func (worker *FileCopyWorker) checkExistingFileFirst(task FileCopyTask, f *os.File) (shouldCopy bool, err error) {
  268. shouldCopy = true
  269. if !*worker.options.checkSize {
  270. return
  271. }
  272. fileStat, err := f.Stat()
  273. if err != nil {
  274. shouldCopy = false
  275. return
  276. }
  277. err = pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  278. request := &filer_pb.LookupDirectoryEntryRequest{
  279. Directory: task.destinationUrlPath,
  280. Name: filepath.Base(f.Name()),
  281. }
  282. resp, lookupErr := client.LookupDirectoryEntry(context.Background(), request)
  283. if lookupErr != nil {
  284. // mostly not found error
  285. return nil
  286. }
  287. if fileStat.Size() == int64(filer.FileSize(resp.Entry)) {
  288. shouldCopy = false
  289. }
  290. return nil
  291. })
  292. return
  293. }
  294. func (worker *FileCopyWorker) uploadFileAsOne(task FileCopyTask, f *os.File) error {
  295. // upload the file content
  296. fileName := filepath.Base(f.Name())
  297. var mimeType string
  298. var chunks []*filer_pb.FileChunk
  299. var assignResult *filer_pb.AssignVolumeResponse
  300. var assignError error
  301. if task.fileMode&os.ModeDir == 0 && task.fileSize > 0 {
  302. mimeType = detectMimeType(f)
  303. data, err := ioutil.ReadAll(f)
  304. if err != nil {
  305. return err
  306. }
  307. // assign a volume
  308. err = util.Retry("assignVolume", func() error {
  309. return pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  310. request := &filer_pb.AssignVolumeRequest{
  311. Count: 1,
  312. Replication: *worker.options.replication,
  313. Collection: *worker.options.collection,
  314. TtlSec: worker.options.ttlSec,
  315. DiskType: *worker.options.diskType,
  316. Path: task.destinationUrlPath,
  317. }
  318. assignResult, assignError = client.AssignVolume(context.Background(), request)
  319. if assignError != nil {
  320. return fmt.Errorf("assign volume failure %v: %v", request, assignError)
  321. }
  322. if assignResult.Error != "" {
  323. return fmt.Errorf("assign volume failure %v: %v", request, assignResult.Error)
  324. }
  325. if assignResult.Url == "" {
  326. return fmt.Errorf("assign volume failure %v: %v", request, assignResult)
  327. }
  328. return nil
  329. })
  330. })
  331. if err != nil {
  332. return fmt.Errorf("Failed to assign from %v: %v\n", worker.options.masters, err)
  333. }
  334. targetUrl := "http://" + assignResult.Url + "/" + assignResult.FileId
  335. uploadOption := &operation.UploadOption{
  336. UploadUrl: targetUrl,
  337. Filename: fileName,
  338. Cipher: worker.options.cipher,
  339. IsInputCompressed: false,
  340. MimeType: mimeType,
  341. PairMap: nil,
  342. Jwt: security.EncodedJwt(assignResult.Auth),
  343. }
  344. uploadResult, err := operation.UploadData(data, uploadOption)
  345. if err != nil {
  346. return fmt.Errorf("upload data %v to %s: %v\n", fileName, targetUrl, err)
  347. }
  348. if uploadResult.Error != "" {
  349. return fmt.Errorf("upload %v to %s result: %v\n", fileName, targetUrl, uploadResult.Error)
  350. }
  351. if *worker.options.verbose {
  352. fmt.Printf("uploaded %s to %s\n", fileName, targetUrl)
  353. }
  354. chunks = append(chunks, uploadResult.ToPbFileChunk(assignResult.FileId, 0))
  355. fmt.Printf("copied %s => http://%s%s%s\n", f.Name(), worker.filerHost, task.destinationUrlPath, fileName)
  356. }
  357. if err := pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  358. request := &filer_pb.CreateEntryRequest{
  359. Directory: task.destinationUrlPath,
  360. Entry: &filer_pb.Entry{
  361. Name: fileName,
  362. Attributes: &filer_pb.FuseAttributes{
  363. Crtime: time.Now().Unix(),
  364. Mtime: time.Now().Unix(),
  365. Gid: task.gid,
  366. Uid: task.uid,
  367. FileSize: uint64(task.fileSize),
  368. FileMode: uint32(task.fileMode),
  369. Mime: mimeType,
  370. Replication: *worker.options.replication,
  371. Collection: *worker.options.collection,
  372. TtlSec: worker.options.ttlSec,
  373. },
  374. Chunks: chunks,
  375. },
  376. }
  377. if err := filer_pb.CreateEntry(client, request); err != nil {
  378. return fmt.Errorf("update fh: %v", err)
  379. }
  380. return nil
  381. }); err != nil {
  382. return fmt.Errorf("upload data %v to http://%s%s%s: %v\n", fileName, worker.filerHost, task.destinationUrlPath, fileName, err)
  383. }
  384. return nil
  385. }
  386. func (worker *FileCopyWorker) uploadFileInChunks(task FileCopyTask, f *os.File, chunkCount int, chunkSize int64) error {
  387. fileName := filepath.Base(f.Name())
  388. mimeType := detectMimeType(f)
  389. chunksChan := make(chan *filer_pb.FileChunk, chunkCount)
  390. concurrentChunks := make(chan struct{}, *worker.options.concurrenctChunks)
  391. var wg sync.WaitGroup
  392. var uploadError error
  393. var collection, replication string
  394. fmt.Printf("uploading %s in %d chunks ...\n", fileName, chunkCount)
  395. for i := int64(0); i < int64(chunkCount) && uploadError == nil; i++ {
  396. wg.Add(1)
  397. concurrentChunks <- struct{}{}
  398. go func(i int64) {
  399. defer func() {
  400. wg.Done()
  401. <-concurrentChunks
  402. }()
  403. // assign a volume
  404. var assignResult *filer_pb.AssignVolumeResponse
  405. var assignError error
  406. err := util.Retry("assignVolume", func() error {
  407. return pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  408. request := &filer_pb.AssignVolumeRequest{
  409. Count: 1,
  410. Replication: *worker.options.replication,
  411. Collection: *worker.options.collection,
  412. TtlSec: worker.options.ttlSec,
  413. DiskType: *worker.options.diskType,
  414. Path: task.destinationUrlPath + fileName,
  415. }
  416. assignResult, assignError = client.AssignVolume(context.Background(), request)
  417. if assignError != nil {
  418. return fmt.Errorf("assign volume failure %v: %v", request, assignError)
  419. }
  420. if assignResult.Error != "" {
  421. return fmt.Errorf("assign volume failure %v: %v", request, assignResult.Error)
  422. }
  423. return nil
  424. })
  425. })
  426. if err != nil {
  427. fmt.Printf("Failed to assign from %v: %v\n", worker.options.masters, err)
  428. }
  429. targetUrl := "http://" + assignResult.Url + "/" + assignResult.FileId
  430. if collection == "" {
  431. collection = assignResult.Collection
  432. }
  433. if replication == "" {
  434. replication = assignResult.Replication
  435. }
  436. uploadOption := &operation.UploadOption{
  437. UploadUrl: targetUrl,
  438. Filename: fileName+"-"+strconv.FormatInt(i+1, 10),
  439. Cipher: worker.options.cipher,
  440. IsInputCompressed: false,
  441. MimeType: "",
  442. PairMap: nil,
  443. Jwt: security.EncodedJwt(assignResult.Auth),
  444. }
  445. uploadResult, err, _ := operation.Upload(io.NewSectionReader(f, i*chunkSize, chunkSize), uploadOption)
  446. if err != nil {
  447. uploadError = fmt.Errorf("upload data %v to %s: %v\n", fileName, targetUrl, err)
  448. return
  449. }
  450. if uploadResult.Error != "" {
  451. uploadError = fmt.Errorf("upload %v to %s result: %v\n", fileName, targetUrl, uploadResult.Error)
  452. return
  453. }
  454. chunksChan <- uploadResult.ToPbFileChunk(assignResult.FileId, i*chunkSize)
  455. fmt.Printf("uploaded %s-%d to %s [%d,%d)\n", fileName, i+1, targetUrl, i*chunkSize, i*chunkSize+int64(uploadResult.Size))
  456. }(i)
  457. }
  458. wg.Wait()
  459. close(chunksChan)
  460. var chunks []*filer_pb.FileChunk
  461. for chunk := range chunksChan {
  462. chunks = append(chunks, chunk)
  463. }
  464. if uploadError != nil {
  465. var fileIds []string
  466. for _, chunk := range chunks {
  467. fileIds = append(fileIds, chunk.FileId)
  468. }
  469. operation.DeleteFiles(func() string {
  470. return copy.masters[0]
  471. }, false, worker.options.grpcDialOption, fileIds)
  472. return uploadError
  473. }
  474. manifestedChunks, manifestErr := filer.MaybeManifestize(worker.saveDataAsChunk, chunks)
  475. if manifestErr != nil {
  476. return fmt.Errorf("create manifest: %v", manifestErr)
  477. }
  478. if err := pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  479. request := &filer_pb.CreateEntryRequest{
  480. Directory: task.destinationUrlPath,
  481. Entry: &filer_pb.Entry{
  482. Name: fileName,
  483. Attributes: &filer_pb.FuseAttributes{
  484. Crtime: time.Now().Unix(),
  485. Mtime: time.Now().Unix(),
  486. Gid: task.gid,
  487. Uid: task.uid,
  488. FileSize: uint64(task.fileSize),
  489. FileMode: uint32(task.fileMode),
  490. Mime: mimeType,
  491. Replication: replication,
  492. Collection: collection,
  493. TtlSec: worker.options.ttlSec,
  494. },
  495. Chunks: manifestedChunks,
  496. },
  497. }
  498. if err := filer_pb.CreateEntry(client, request); err != nil {
  499. return fmt.Errorf("update fh: %v", err)
  500. }
  501. return nil
  502. }); err != nil {
  503. return fmt.Errorf("upload data %v to http://%s%s%s: %v\n", fileName, worker.filerHost, task.destinationUrlPath, fileName, err)
  504. }
  505. fmt.Printf("copied %s => http://%s%s%s\n", f.Name(), worker.filerHost, task.destinationUrlPath, fileName)
  506. return nil
  507. }
  508. func detectMimeType(f *os.File) string {
  509. head := make([]byte, 512)
  510. f.Seek(0, io.SeekStart)
  511. n, err := f.Read(head)
  512. if err == io.EOF {
  513. return ""
  514. }
  515. if err != nil {
  516. fmt.Printf("read head of %v: %v\n", f.Name(), err)
  517. return ""
  518. }
  519. f.Seek(0, io.SeekStart)
  520. mimeType := http.DetectContentType(head[:n])
  521. if mimeType == "application/octet-stream" {
  522. return ""
  523. }
  524. return mimeType
  525. }
  526. func (worker *FileCopyWorker) saveDataAsChunk(reader io.Reader, name string, offset int64) (chunk *filer_pb.FileChunk, collection, replication string, err error) {
  527. var fileId, host string
  528. var auth security.EncodedJwt
  529. if flushErr := pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  530. ctx := context.Background()
  531. assignErr := util.Retry("assignVolume", func() error {
  532. request := &filer_pb.AssignVolumeRequest{
  533. Count: 1,
  534. Replication: *worker.options.replication,
  535. Collection: *worker.options.collection,
  536. TtlSec: worker.options.ttlSec,
  537. DiskType: *worker.options.diskType,
  538. Path: name,
  539. }
  540. resp, err := client.AssignVolume(ctx, request)
  541. if err != nil {
  542. return fmt.Errorf("assign volume failure %v: %v", request, err)
  543. }
  544. if resp.Error != "" {
  545. return fmt.Errorf("assign volume failure %v: %v", request, resp.Error)
  546. }
  547. fileId, host, auth = resp.FileId, resp.Url, security.EncodedJwt(resp.Auth)
  548. collection, replication = resp.Collection, resp.Replication
  549. return nil
  550. })
  551. if assignErr != nil {
  552. return assignErr
  553. }
  554. return nil
  555. }); flushErr != nil {
  556. return nil, collection, replication, fmt.Errorf("filerGrpcAddress assign volume: %v", flushErr)
  557. }
  558. uploadOption := &operation.UploadOption{
  559. UploadUrl: fmt.Sprintf("http://%s/%s", host, fileId),
  560. Filename: name,
  561. Cipher: worker.options.cipher,
  562. IsInputCompressed: false,
  563. MimeType: "",
  564. PairMap: nil,
  565. Jwt: auth,
  566. }
  567. uploadResult, flushErr, _ := operation.Upload(reader, uploadOption)
  568. if flushErr != nil {
  569. return nil, collection, replication, fmt.Errorf("upload data: %v", flushErr)
  570. }
  571. if uploadResult.Error != "" {
  572. return nil, collection, replication, fmt.Errorf("upload result: %v", uploadResult.Error)
  573. }
  574. return uploadResult.ToPbFileChunk(fileId, offset), collection, replication, nil
  575. }