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 }