github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/chunked_upload_form_builder.go (about)

     1  package sdk
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"encoding/json"
     7  	"io"
     8  	"mime/multipart"
     9  	"sync"
    10  
    11  	"github.com/0chain/gosdk/zboxcore/client"
    12  
    13  	"golang.org/x/crypto/sha3"
    14  )
    15  
    16  // ChunkedUploadFormBuilder build form data for uploading
    17  type ChunkedUploadFormBuilder interface {
    18  	// build form data
    19  	Build(
    20  		fileMeta *FileMeta, hasher Hasher, connectionID string,
    21  		chunkSize int64, chunkStartIndex, chunkEndIndex int,
    22  		isFinal bool, encryptedKey, encryptedKeyPoint string, fileChunksData [][]byte,
    23  		thumbnailChunkData []byte, shardSize int64,
    24  	) (blobberData, error)
    25  }
    26  
    27  // ChunkedUploadFormMetadata upload form metadata
    28  type ChunkedUploadFormMetadata struct {
    29  	FileBytesLen         int
    30  	ThumbnailBytesLen    int
    31  	ContentType          string
    32  	FixedMerkleRoot      string
    33  	ValidationRoot       string
    34  	ThumbnailContentHash string
    35  }
    36  
    37  // CreateChunkedUploadFormBuilder create ChunkedUploadFormBuilder instance
    38  func CreateChunkedUploadFormBuilder() ChunkedUploadFormBuilder {
    39  	return &chunkedUploadFormBuilder{}
    40  }
    41  
    42  type chunkedUploadFormBuilder struct {
    43  }
    44  
    45  const MAX_BLOCKS = 80 // 5MB(CHUNK_SIZE*80)
    46  
    47  func (b *chunkedUploadFormBuilder) Build(
    48  	fileMeta *FileMeta, hasher Hasher, connectionID string,
    49  	chunkSize int64, chunkStartIndex, chunkEndIndex int,
    50  	isFinal bool, encryptedKey, encryptedKeyPoint string, fileChunksData [][]byte,
    51  	thumbnailChunkData []byte, shardSize int64,
    52  ) (blobberData, error) {
    53  
    54  	metadata := ChunkedUploadFormMetadata{
    55  		ThumbnailBytesLen: len(thumbnailChunkData),
    56  	}
    57  
    58  	var res blobberData
    59  
    60  	if len(fileChunksData) == 0 {
    61  		return res, nil
    62  	}
    63  
    64  	numBodies := len(fileChunksData) / MAX_BLOCKS
    65  	if len(fileChunksData)%MAX_BLOCKS > 0 {
    66  		numBodies++
    67  	}
    68  	dataBuffers := make([]*bytes.Buffer, 0, numBodies)
    69  	contentSlice := make([]string, 0, numBodies)
    70  
    71  	formData := UploadFormData{
    72  		ConnectionID: connectionID,
    73  		Filename:     fileMeta.RemoteName,
    74  		Path:         fileMeta.RemotePath,
    75  
    76  		ActualSize: fileMeta.ActualSize,
    77  
    78  		ActualThumbHash: fileMeta.ActualThumbnailHash,
    79  		ActualThumbSize: fileMeta.ActualThumbnailSize,
    80  
    81  		MimeType: fileMeta.MimeType,
    82  
    83  		// IsFinal:           isFinal,
    84  		ChunkSize:         chunkSize,
    85  		ChunkStartIndex:   chunkStartIndex,
    86  		ChunkEndIndex:     chunkEndIndex,
    87  		UploadOffset:      chunkSize * int64(chunkStartIndex),
    88  		Size:              shardSize,
    89  		EncryptedKeyPoint: encryptedKeyPoint,
    90  		EncryptedKey:      encryptedKey,
    91  		CustomMeta:        fileMeta.CustomMeta,
    92  	}
    93  
    94  	for i := 0; i < numBodies; i++ {
    95  
    96  		startRange := i * MAX_BLOCKS
    97  		endRange := startRange + MAX_BLOCKS
    98  		if endRange > len(fileChunksData) {
    99  			endRange = len(fileChunksData)
   100  		}
   101  		buff := formDataPool.Get()
   102  		bufSize := (CHUNK_SIZE * (endRange - startRange)) + 1024
   103  		if cap(buff.B) < bufSize {
   104  			buff.B = make([]byte, 0, bufSize)
   105  		}
   106  		bodyBuf := buff.B
   107  
   108  		body := bytes.NewBuffer(bodyBuf)
   109  		formWriter := multipart.NewWriter(body)
   110  		defer formWriter.Close()
   111  
   112  		uploadFile, err := formWriter.CreateFormFile("uploadFile", formData.Filename)
   113  		if err != nil {
   114  			return res, err
   115  		}
   116  
   117  		for _, chunkBytes := range fileChunksData[startRange:endRange] {
   118  			_, err = uploadFile.Write(chunkBytes)
   119  			if err != nil {
   120  				return res, err
   121  			}
   122  
   123  			err = hasher.WriteToFixedMT(chunkBytes)
   124  			if err != nil {
   125  				return res, err
   126  			}
   127  
   128  			err = hasher.WriteToValidationMT(chunkBytes)
   129  			if err != nil {
   130  				return res, err
   131  			}
   132  
   133  			metadata.FileBytesLen += len(chunkBytes)
   134  		}
   135  
   136  		if isFinal && i == numBodies-1 {
   137  			err = hasher.Finalize()
   138  			if err != nil {
   139  				return res, err
   140  			}
   141  
   142  			var (
   143  				wg      sync.WaitGroup
   144  				errChan = make(chan error, 2)
   145  			)
   146  			wg.Add(2)
   147  			go func() {
   148  				formData.FixedMerkleRoot, err = hasher.GetFixedMerkleRoot()
   149  				if err != nil {
   150  					errChan <- err
   151  				}
   152  				wg.Done()
   153  			}()
   154  			go func() {
   155  				formData.ValidationRoot, err = hasher.GetValidationRoot()
   156  				if err != nil {
   157  					errChan <- err
   158  				}
   159  				wg.Done()
   160  			}()
   161  			wg.Wait()
   162  			close(errChan)
   163  			for err := range errChan {
   164  				return res, err
   165  			}
   166  			actualHashSignature, err := client.Sign(fileMeta.ActualHash)
   167  			if err != nil {
   168  				return res, err
   169  			}
   170  
   171  			validationRootSignature, err := client.Sign(actualHashSignature + formData.ValidationRoot)
   172  			if err != nil {
   173  				return res, err
   174  			}
   175  
   176  			formData.ActualHash = fileMeta.ActualHash
   177  			formData.ActualFileHashSignature = actualHashSignature
   178  			formData.ValidationRootSignature = validationRootSignature
   179  			formData.ActualSize = fileMeta.ActualSize
   180  
   181  		}
   182  
   183  		thumbnailSize := len(thumbnailChunkData)
   184  		if thumbnailSize > 0 && i == 0 {
   185  
   186  			uploadThumbnailFile, err := formWriter.CreateFormFile("uploadThumbnailFile", fileMeta.RemoteName+".thumb")
   187  			if err != nil {
   188  
   189  				return res, err
   190  			}
   191  
   192  			thumbnailHash := sha3.New256()
   193  			thumbnailWriters := io.MultiWriter(uploadThumbnailFile, thumbnailHash)
   194  			_, err = thumbnailWriters.Write(thumbnailChunkData)
   195  			if err != nil {
   196  				return res, err
   197  			}
   198  			_, err = thumbnailHash.Write([]byte(fileMeta.RemotePath))
   199  			if err != nil {
   200  				return res, err
   201  			}
   202  			formData.ActualThumbSize = fileMeta.ActualThumbnailSize
   203  			formData.ThumbnailContentHash = hex.EncodeToString(thumbnailHash.Sum(nil))
   204  
   205  		}
   206  		if i > 0 {
   207  			formData.UploadOffset = formData.UploadOffset + chunkSize*int64(MAX_BLOCKS)
   208  		}
   209  
   210  		err = formWriter.WriteField("connection_id", connectionID)
   211  		if err != nil {
   212  			return res, err
   213  		}
   214  
   215  		if isFinal && i == numBodies-1 {
   216  			formData.IsFinal = true
   217  		}
   218  
   219  		uploadMeta, err := json.Marshal(formData)
   220  		if err != nil {
   221  			return res, err
   222  		}
   223  
   224  		err = formWriter.WriteField("uploadMeta", string(uploadMeta))
   225  		if err != nil {
   226  			return res, err
   227  		}
   228  
   229  		contentSlice = append(contentSlice, formWriter.FormDataContentType())
   230  		dataBuffers = append(dataBuffers, body)
   231  	}
   232  	metadata.FixedMerkleRoot = formData.FixedMerkleRoot
   233  	metadata.ValidationRoot = formData.ValidationRoot
   234  	metadata.ThumbnailContentHash = formData.ThumbnailContentHash
   235  	res.dataBuffers = dataBuffers
   236  	res.contentSlice = contentSlice
   237  	res.formData = metadata
   238  	return res, nil
   239  }