百度智能云

All Product Document

          Object Storage

          Web Direct Transmission Practice

          Scenarios Overview

          User upload through the Web is a typical application scenario in BOS usage requirements. In this scenario, users usually use the model transferred by the application server to upload files.

          BOS11.png

          1.The user first uploads the file to the application server via the Web.

          2.The application server then uploads the file to BOS.

          In this model, there are three disadvantages:

          • Slow upload. Because it needs to transfer through the application server, the network transmission has doubled compared to the client data direct upload to BOS.
          • Poor scalability. As the number of users increasing, the application server may become a transmission bottleneck.
          • High cost. The deployment and maintenance of the application server requires a certain cost. If the client data is direct uploaded to the BOS, the cost of the application server will be saved, and the traffic of uploading by the BOS is free.

          Therefore, in this scenario, we recommend that you use the bce-bos-uploader tool to implement the direct upload from the client to BOS.

          BOS12.png

          Bce Bos Uploader

          Baidu Cloud Engine BOS Uploader (bce-bos-uploader) is a ui component developed by Baidu AI Cloud based on Javascript SDK. It is specially provided for the convenience of users to develop web direct uploading applications. Using this tool can complete the connection with BOS services with a few lines of code. The demo operation interface of bce-bos-uploader is as follows:

          Supported browsers

          1.Based on Xhr2 and File API, it can support IE10+, Firefox, Chrome and the latest versions of Opera.

          2.With the help of Postobject interface, it can support the lower version of IE (6,7,8,9), please refer to [Advanced Chapter 2: Handle IE lower version through Postobject interface](# Advanced Chapter 2: Handle IE lower versions through the Postobject interface).

          Supported configuration parameters

          Name Is it necessary to fill in Default value Description
          bos_bucket Y None Need to upload to bucket
          browse_button Y None Need to be initialized < input type="file"/>
          uptoken_url N None The URL used to calculate the signature, which needs to support JSONP
          bos_endpoint N <http://bj.bcebos.com> BOS server address
          bos_ak N None If uptoken_url is not set, ak and sk must be configured to work.
          bos_sk N None If uptoken_url is not set, ak and sk must be configured to work.
          uptoken N None If it is temporary ak and sk, the sts token value must be set through this parameter
          multi_selection N false Whether the multiple files can be selected
          auto_start N false After selecting a file, whether to upload automatically
          max_file_size N 100M The largest file that can be selected. If this value is exceeded, it will be ignored.
          bos_multipart_min_size N 10M If this value is exceeded, the strategy of multi-part uploading is used. If you want all files to be multi-part uploaded, set this value to 0.
          chunk_size N 4M When multi-part uploading, the size of each part (this value is meaningless if it do not switch to the multi-part upload strategy)
          accept N None Supported optional file types, comma-separated suffix names, for example: txt,pdf,doc,docx
          flash_swf_url N None mOxie Flash file address If you need to support lower versions of IE, you must set this parameter

          For more detailed usage information, please refer to: Bce-bos-uploader Statement.

          bce-bos-upload supports signature methods: default, STS and PostObiect.

          • The default signature method is to use the AK/SK signature method to directly upload files to the BOS in the browser. When the file is directly upload to the BOS server through the browser, if AK and SK are exposed on the page, it will cause security problems. If an attacker obtains AK and SK, they can perform arbitrary operations on the data on the BOS. In order to reduce the risk of leakage, it is recommended that users use STS temporary authentication.
          • Using STS signatures is more secure and flexible. It can flexibly and precisely control the user’s use permission, and it is not necessary to call the back-end interface every time a request is made, and it is not necessary to request a new Security Token within the validity period.
          • Because the lower version of IE (IE6,7,8,9) has incomplete support for the HTML5 API, in order to implement the file direct uploading function in thoese browsers, BOS has developed Postobject Interface, support multipart/form-data request format, easy to upload files to BOS server on lower version of IE.

          Foundation Chapter: Upload Files Directly to BOS in the Browser

          With bce-bos-uploader, you can refer to the following to complete how to upload files directly to BOS in the browser. Process flow:

          1.Enable bucket's cross-domain access settings

          2.Query ak/sk

          3.Initialize bce-bos-uploader parameter

          Enable Bucket's Across-domain Access

          Due to browser security restrictions, if you want to access the BOS service directly in the browser, you must correctly set the cross-domain function related to bucket. The setting method is as follows:

          1. Log in to Baidu AI Cloud Console.
          2. Select bucket and enter the bucket management page.
          3. Click [bucket Properties]on the left to enter the bucket configuration page.
          4. Click [CORS Settings]on the right to enter the CORS settings page.
          5. Click the "Add Rule" button to add one or more CORS rules.

          image.png

          Query AK/SK

          You can check the information of AK and SK in "Security Authentication" under the account in the upper right corner of the home page of Baidu AI Cloud Console, and you can also view it in bucket Management. For detailed operations, please refer to Manage ACCESSKEY.

          Get Bce-bos-uploader

          There are two ways to get the code of bce-bos-uploader:

          • The first: Install via npm

              npm install @baiducloud/bos-uploader 
          • The second: Directly reference the resources on the CDN (for testing purposes, not recommended for production environments)

              <script src=" https://bj.bcebos.com/v1/bce-cdn/lib/@baiducloud/bos-uploader/<version>/bce-bos-uploader.bundle.min.js"></script> 

          Initialize Bce-bos-uploader

          <!doctype html>
          <html>
            <head>
              <meta charset="utf-8" />
              <title>bce-bos-uploader simple demo</title>
              <!--[if lt IE 8]><script src="https://unpkg.com/json3@3.3.2/lib/json3.min.js"></script><![endif]-->
              <!--[if lt IE 9]><script src="https://unpkg.com/js-polyfills@0.1.42/es5.js"></script><![endif]-->
              <!--[if lt IE 10]><script src="https://unpkg.com/mOxie@1.5.7/bin/js/moxie.min.js"></script><![endif]-->
              <script src="https://unpkg.com/jquery@3.3.1/dist/jquery.min.js"></script>
              <script src="https://bce.bdstatic.com/lib/@baiducloud/bos-uploader/1.4.0-rc.0/bce-bos-uploader.bundle.min.js"></script>
            </head>
            <body>
              <input type="file" id="file" >
              <button type="submit">Start uploading</button>
              <script>
                var uploader = new baidubce.bos.Uploader({
                browse_button: '#file',
                bos_bucket: '<your bucket>',
                bos_endpoint: '<your host>',
                bos_ak: '<your ak>', 
                bos_sk: '<your sk>',
                max_file_size: '1Gb',
                init: {
                  FileUploaded: function (_, file, info) {
                    var bucket = info.body.bucket;
                    var object = info.body.object;
                    var url = '<your host>' + bucket + '/' + object;
                    $(document.body).append($('<div><a href="' + url + '">' + url + '</a></div>'));
                  },
                  UploadComplete: function() {
                    $(document.body).append('<div>Finish uploading!</div>');
                  }
                }
              });
               $('button[type=submit]').click(function () {
                uploader.start();
                return false;
              });
          
              </script>
            </body>
          </html>

          Save the above code as index.html, the webserver will be enabled to access this page

          Enable Webserver

          • Enable via PHP

              php -S 0.0.0.0:9999 
          • Enable via Python

              python -m SimpleHTTPServer 9999 
          • Please refer to related documents for other methods

          After enabling the webserver, visit http://localhost: 9999/index.html in the browser to open the previous page and start testing whether it can upload normally.

          Advanced Chapter 1: STS Interim Certification

          Bce-bos-uploader supports STS (Security Token Service) temporary authorization. The server generates a set of temporary AK/SK with specific operation permissions and certain timeliness. This set of temporary AK/SK can be exposed to the browser and used directly. The user only needs to set the AK/SK and SessionToken returned by the server to the bos-ak, bos-sk and uptoken parameters corresponding to bce-bos-uploader. The following figure briefly introduces the entire business interaction process. For the introduction of STS, please refer to Temporary Authorized Access.

          web-3_8285df9.png

          The code implementation is divided into two parts, the application server and the client. The implementation process is as follows:

          1.Configure the application server. Taking Nodejs implementation as an example, the server will return AK/SK SessionToken.

          2.Configure the browser and initialize the bce-bos-uploader parameters based on the AK/SK/SessionToken returned by the server.

          Application Server Nodejs Implementation

          var http = require('http');
          var url = require('url');
          var util = require('util');
          
          var STS = require('@baiducloud/sdk').STS;
          
          var kCredentials = {
              ak: 'Your AK',
              sk: 'Your SK'
          };
          
          function buildStsResponse() {
              var stsClient = new STS({
                  credentials: kCredentials,
                  region: 'bj'
              });
              return stsClient.getSessionToken(60 * 60 * 24, {
                  accessControlList: [{
                      service: 'bce:bos',
                      resource: ['bce-javascript-sdk-demo-test'],
                      region: '*',
                      effect: 'Allow',
                      permission: ['READ', 'WRITE']
                  }]
              }).then(function (response) {
                  var body = response.body;
                  return {
                      AccessKeyId: body.accessKeyId,
                      SecretAccessKey: body.secretAccessKey,
                      SessionToken: body.sessionToken,
                      Expiration: body.expiration
                  };
              });
          }
          
          http.createServer(function (req, res) {
              console.log(req.url);
          
              var query = url.parse(req.url, true).query;
          
              var promise = null;
          
              if (query.sts) {
                  promise = buildStsResponse();
              }
          
              promise.then(function (payload) {
                  res.writeHead(200, {
                      'Content-Type': 'text/javascript; charset=utf-8',
                      'Access-Control-Allow-Origin': '*'
                  });
          
                  if (query.callback) {
                      res.end(util.format('%s(%s)', query.callback, JSON.stringify(payload)));
                  }
                  else {
                      res.end(JSON.stringify(payload));
                  }
              });
          }).listen(1337);
          console.log('Server running at http://0.0.0.0:1337/');

          On the server, create a stsClient instance in a similar way to creating a bosClient instance. For the stsClient instance, there is mainly one method, which is getSessionToken. This method receives two parameters, the first parameter is the validity period of the temporary authorization, in seconds; the second unit is the specific permission control, see STS Service Interface.

          This method will asynchronously access the STS authorization server and return a promise object. The STS authorization server will return something similar to the following:

          {   
              body: {         
                  "accessKeyId": "d87a16e5ce1d47c1917b38ed03fbb329", 
                  "secretAccessKey": "e9b6f59ce06c45cdaaea2296111dab46", 
                   "sessionToken": "MjUzZjQzNTY4OTE0NDRkNjg3N2E4YzJhZTc4YmU5ZDh8AAAAABwCAAB/HfHDVV2bu5xUf6rApt2YdSLG6+21UTC62EHvIuiaamtuMQQKNkR9PU2NJGVbuWgBn8Ot0atk0HnWYQGgwgyew24HtbrX3GFiR/cDymCowm0TI6OGq7k8pGuBiCczT8qZcarH7VdZBd1lkpYaXbtP7wQJqiochDXrswrCd+J/I2CeSQT6mJiMmvupUV06R89dWBL/Vcu7JQpdYBk0d5cp2B+gdaHddBobevlBmKQw50/oOykJIuho4Wn7FgOGPMPdod0Pf0s7lW/HgSnPOjZCgRl0pihs197rP3GWpnlJRyfdCY0g0GFG6T0/FsqDbxbi8lWzF1QRTmJzzh2Tax8xoPFKGMbpntp//vGP7oPYK1JoES34TjcdcZnLzIRnVIGaZAzmZMUhPEXE5RVX1w8jPEXMJJHSrFs3lJe13o9Dwg==",         
                  "createTime": "2016-02-16T14:01:29Z",         
                  "expiration": "2016-02-16T15:41:29Z",         
                  "userId": "5e433c4a8fe74765a7ec6fc147e25c80"     
              } 
          } 

          The server needs to send the accessKeyId, secretAccessKey and sessionToken fields to the browser.

          Configure Browser-side Bce-bos-uploader Parameters

          When using the STS temporary authorization mechanism, you only need to introduce the parameters accessKeyId, secretAccessKey and sessionToken mentioned above when each service is initialized.

          <!doctype html>
          <html>
            <head>
              <meta charset="utf-8" />
              <title>bce-bos-uploader simple demo</title>
              <!--[if lt IE 8]><script src="http://websdk.cdn.bcebos.com/bos/json3/lib/json3.min.js"></script><![endif]-->
              <!--[if lt IE 9]><script src="http://websdk.cdn.bcebos.com/bos/js-polyfills/es5.js"></script><![endif]-->
              <!--[if lt IE 10]><script src="http://websdk.cdn.bcebos.com/bos/moxie/bin/js/moxie.js"></script><![endif]-->
              <!-- BOS does not provide jquery.min.js, developers can introduce online or local resources by themselves -->
              <script src="./node_modules/jquery/dist/jquery.min.js"></script
              <!-- Introduce bce-bos-uploader.bundle.js, it is recommended to introduce local resources after successful installation via npm -->
              <script src="./node_modules/@baiducloud/bos-uploader/dist/bce-bos-uploader.bundle.js"></script>
            </head>
            <body>
            
              <input type="file" id="file" >
              <script>
              var uploader = new baidubce.bos.Uploader({
                browse_button: '#file',
                bos_bucket: '<your bucket>',
                bos_endpoint: 'http://bj.bcebos.com',
                bos_ak: '<your ak>', 
                bos_sk: '<your sk>',
                uptoken: '<your sessionToken>'
              });
              </script>
            </body>
          </html>

          Advanced Chapter 2: Handle IE Lower Versions through the Postobject Interface

          Because the lower version of IE (IE8, IE9) has incomplete support for the html5, in order to implement the function of file direct uploading in these browsers, BOS has developed the PostObject interface, which can upload files to the BOS server through a multipart/form-data format. In the signature mode of the PostObject interface, the application server generates a signature for the policy and returns it to the client.

          web-4_4ee1fb6.png

          bce-bos-uploader has implemented support for this interface, and additional configuration work is required before use:

          Configure the Application Server

          1.Upload crossdomain.xml

          Based on html5 cross-domain solution, we need to set cross-domain paradigm (CORS); if you use flash to complete cross-domain data interaction, you need to set crossdomain.xml, you can directly save the following content as crossdomain.xml, and then upload it to the root directory of the bucket.

          <?xml version="1.0" encoding="UTF-8" ?>
          <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
          <cross-domain-policy>
            <site-control permitted-cross-domain-policies="all"/>
            <allow-access-from domain="*" secure="false" />
            <allow-http-request-headers-from domain="*" headers="*" secure="false"/>
          </cross-domain-policy>

          If the bucket is private, you need to set the bucket to custom permissions to allow crossdomain.xml to be public-read. Select a bucket on the Console console and enter the "Basic Settings" tab, select "Modify Configuration" corresponding to "bucket Permission Settings", modify the bucket permission setting to "Custom Permissions" and add permissions.

          image.png

          2.The server returns the uptoken_url parameter.

          When using Postobject to process the lower version of IE, you need to configure the uptoken_url parameter in bce-bos-uploader.

          Configure Browser-side Bce-bos-uploader Parameters

          Principle 1: Upload Files Directly to BOS in the Browser

          If you do not use bce-bos-uploader, you can refer to the following to complete how to upload files directly to BOS in the browser. Process flow:

          1.Enable bucket's cross-domain access settings

          2.Query ak/sk

          3.Initialize BosClient

          4.Handle upload logic

          Enable Bucket's Across-origin Access

          Due to browser security restrictions, if you want to access the BOS service directly in the browser, you must correctly set the cross-origin function related to bucket. For the setting method, please refer to Enable Bucket Cross-origin Access.

          Initial Settings

          var bosConfig = { 
              credentials: { 
                   ak: 'Query your ak' from Baidu AI Cloud Console', 
                   sk: 'Query the sk' corresponding to the above ak from Baidu AI Cloud Console' 
               }, 
               endpoint: 'http://bj.bcebos.com' // Configure the corresponding endpoint based on the region where you choose the bos service 
           }; 
           var bucket = 'bce-javascript-sdk-demo-test'; // Set the bucket you want to operate 
           var client = new baidubce.sdk.BosClient(bosConfig); 

          In the future, we can use the client instance to perform BOS related operations.

          Upload Logic

          We can complete the file upload operation by calling client.putObjectFromBlob (bucket, key, blob, options).

          This function supports 4 parameters, of which options are optional. If you need to manually set the Content-Type of the file, you can put it in the options parameter. If no set manually, the default Content-Type is application/oceat-stream. In addition, you can get some commonly used Content-Type based on the suffix name by calling baidubce.sdk.MimeType.guess (ext).

          Note: Because of a Firefox compatibility issue, if the uploaded file is the type of text/* , Firefox will automatically add charset = utf-8.Therefore, when we set the Content-Type for options, we need to manually add charset = utf-8, otherwise the signature calculated by the browser and the signature calculated by the server will be inconsistent, causing the upload to fail.

          // Listen to file upload events, assuming the page has:<input type="file" id="upload" /> $('#upload').on('change', function (evt) {
               var file = evt.target.files[0]; // Obtain the file to upload
               var key = file.name; // The key when saving to bos, you can change it, the default is to use the file name as the key
               var blob = file;
          
               var ext = key.split(/\./g).pop();
               var mimeType = baidubce.sdk.MimeType.guess(ext);
               if (/^text\//.test(mimeType)) {
                   mimeType += '; charset=UTF-8';
               }
               var options = {
                   'Content-Type': mimeType
               };
          
                client.putObjectFromBlob(bucket, key, blob, options)
                   .then(function (res) {
                       // Upload completed, add your code 
                       console.log ('Upload succeeded');
                   })
                   .catch(function (err) {
                       // Failed to upload, add your code 
                       console.error(err); 
                   });
            });

          If you want to know the progress of the current upload, you can listen to the progress event.

           client.on('progress', function (evt) 
           { 
               // Listen upload progress 
               if (evt.lengthComputable) 
           { 
                   // Add your code 
                   var percentage = (evt.loaded / evt.total) * 100; 
                   console.log ('Uploading, uploaded' + percentage + '%'); 
               } 
           }); 

          Principle 2: Chunked Uploading of Large Files

          When the browser is used to upload files to BOS, the user needs to divide files into parts and then uploads them if files are too large. It is possible to encounter many problems such as page closure, browser crash and linkage interrupt, which consequently result in failure to upload. BOS supports multipart upload and breakpoint resume functions. Please see "Multipart Upload of object" for multipart upload.. Below are methods to realize "breakpoint resume".

          Realization Principle

          When we use mulitipartUpload of files, BOS will first allocate a uploadId for the upload process. Afterwards, we divide a file into several parts and each part is independently uploaded. After a part is uploaded, BOS will generate an eTag for this part. As all parts are uploaded, BOS service will find the correct parts out according to these eTag and uploadId, and combine them into original file.

          During this process, BOS doesn't require uploading of all parts at one blow. Instead, uploading of parts can be conducted for many times. That is to say, during upload process, we only need to re-upload unsuccessfully uploaded parts without requirements to re-upload from scratch if the page is accidentally closed. Of course, the premise is that we need to save uploadId which is uploaded at this time and eTag of the uploaded part (But a more promising method is to inquire more precise uploaded block information though listParts Interface). Before a part is uploaded, you can check whether this part has been uploaded. If it has been uploaded successfully at one time, then it is permitted to directly skip upload process of this part.

          As to storage of uploadId, it is needed to meet the requirement that it is not affected by page closure. Relatively desired method is to store it in localStorage.

          Local Storage

          When uploadId is saved, we need to specify a key for it to make different files separated from various upload processes. In this example, file name, file size, partition size, bucket name and object name are adopted to constitute this key:

          var generateLocalKey = function (blob, chunkSize, bucket, object) { 
               return [blob.name, blob.size, chunkSize, bucket, object].join('&'); 
           }; 

          Note: The key generated in this way is not accurate. If two same file names as well as the same file size with different file contents are selected during two upload processes, use of this way is not able to differentiate these two files. A more serious way is to calculate MD5 according to file name and content, and consider it as the key.

          In terms of storage mode, we choose localStorage:

          var getUploadId = function (key) { 
               return localStorage.getItem(key); 
           };  
          var setUploadId = function (key, uploadId) { 
               return localStorage.setItem(key, uploadId); 
           };  
          var removeUploadId = function (key) { 
               return localStorage.removeItem(key); 
           }; 

          Initialize Multipart Upload

          There are two possibilities during initialization of multipart upload:

          • If there has existed uploadId of this file, you have to skip the method initiateMultipartUpload() and call listParts() to obtain block information which has been uploaded instead;
          • If there is no uploadId of this file, you have to call the method initiateMultipartUpload() to acquire new uploadId and save this uploadId in localStorage.
              // ...Omit BoClient initialization process 
              // var bosClient = new BosClient(bosConfig); 
              
                var initiateMultipartUpload = function (file, chunkSize, bucket, object) { 
                   // Generate the key for localStorage in compliance with files 
                   var key = generateLocalKey(file, chunkSize, bucket, object); 
                    // Obtain corresponding `uploadId` 
                   var uploadId = getUploadId(key); 
                    if (uploadId) { 
                       // Existence of `uploadId` reflects that there is uncompleted multipart upload. 
                       // Then call `listParts()` to obtain uploaded block information. 
                       return BosClient.listParts(bucket, object, uploadId) 
                           .then(function (response) { 
                               // Response.body.parts includes block information which has been uploaded 
                               response.body.uploadId = uploadId; 
                               return response; 
                           }); 
                   } 
                   else { 
                       // If `uploadId` does not exist, a normal process should be used to conduct initialization 
                       return BosClient.initiateMultipartUpload(bucket, object) 
                           .then(function (response) { 
                               // Response.body.uploadId is newly generated `uploadId` 
                               response.body.parts = []; 
                                // In order to make breakpoint resume available for next time, we need to save newly generated `uploadId` 
                               setUploadId(key, response.body.uploadId); 
                               return response; 
                           }); 
                   } 
               } 

          Multipart Upload

          During segmentation and division of large files, we can compare them with uploaded blocked list to determine whether it is really needed to conduct upload.

          function getEtag(partNumber, parts){ 
               // Find out eTag of part in specific partNumber from the part list which has been uploaded 
               for(var i = 0, l = parts.length; i< l; i++){ 
                   if (parts[i].partNumber === partNumber) { 
                       return parts[i].eTag; 
                   } 
               } 
               return null; 
           } 
            function getTasks (file, uploadId, chunkSize, bucket, object, parts) { 
               var leftSize = file.size; 
               var offset = 0; 
               var partNumber = 1; 
          
               var tasks = []; 
          
               while (leftSize > 0) { 
                   var partSize = Math.min(leftSize, chunkSize); 
                   var task = { 
                       file: file, 
                       uploadId: uploadId, 
                       bucket: bucket, 
                       object: object, 
                       partNumber: partNumber, 
                       partSize: partSize, 
                       start: offset, 
                       stop: offset + partSize - 1 
                   }; 
          
                    // If etag of this block is founded out from the blocked list which has been uploaded, it should be recorded 
                   var etag = getEtag(partNumber, parts); 
                   if (etag){ 
                       task.etag = etag; 
                   } 
                    tasks.push(task); 
                    leftSize -= partSize; 
                   offset += partSize; 
                   partNumber += 1; 
               } 
                return tasks; 
           } 

          It is required to decide whether it is necessary to upload in accordance with whether the etag filed having been carried during the processing of multipart upload:

          function uploadPartFile(state, bosClient) { 
               return function(task, callback) { 
                   if (task.etag) { 
                       // If there exists etag filed, it is needed to directly skip and upload 
                       callback(null, { 
                           http_headers: { 
                               etag: task.etag 
                           }, 
                           body: {} 
                       }); 
                   } 
                   else { 
                       // Otherwise, conduct upload. 
                       var blob = task.file.slice(task.start, task.stop + 1); 
                       bosClient.uploadPartFromBlob(task.BucketName, task.key, task.uploadId, task.partNumber, task.partSize, blob) 
                           .then(function(res) { 
                               ++state.loaded; 
                              callbacknull,res); 
                           }) 
                           .catch(function(err) { 
                               callback(err); 
                           }); 
                   } 
               }; 
           } 

          Process Code

          Although we have made some slight modification to the code of each step, code of the whole process is similar to that of mulipart upload:

          var chunkSize = 5 * 1024 * 1024; // Block size 
          var uploadId; 
          initiateMultipartUpload(file, chunkSize, bucket, object) 
               .then(function(response) { 
                   uploadId = response.body.uploadId; // UploadId may be just generated by the server, or obtained from localStorage 
                   var parts = response.body.parts||  []; // Uploaded blocked list. It becomes an empty array if it is newly uploaded 
                   var deferred = sdk.Q.defer(); 
                   var tasks = getTasks(blob, uploadId, chunkSize, bucket, key, parts); 
                   var state = { 
                       lengthComputable: true, 
                       loaded: parts.length, // Uploaded block number 
                       total: tasks.length 
                   }; 
                   // If uploaded block number is greater than, file upload progress can be modified firstly 
                   bosClient.emit('progress', state); 
                   // To manage multipart upload, async (https://github.com/caolan/async) library is used for asynchronous processing. 
                   var THREADS = 2; // Number of parts uploaded simultaneously 
                   async.mapLimit(tasks, THREADS, uploadPartFile(state, bosClient), function(err, results) { 
                       if (err) { 
                           deferred.reject(err); 
                       } else { 
                           deferred.resolve(results); 
                       } 
                   }); 
                   return deferred.promise; 
               }) 
               .then(function(allResponse) { 
                   var partList = []; 
                   allResponse.forEach(function(response, index) { 
                       // Generate list of parts 
                       partList.push({ 
                           partNumber: index + 1, 
                           eTag: response.http_headers.etag 
                       }); 
                   }); 
          
                   // After uploading of all chunks is completed, it is allowed to delete corresponding `uploadId` 
                   removeUploadId(key, uploadId); 
          
                   return bosClient.completeMultipartUpload(bucket, key, uploadId, partList); // Complete Upload. 
               }) 
               .then(function (res) { 
                   // Uploaded successfully 
               }) 
               .catch(function (err) { 
                   // Failed to upload, add your code 
                   console.error(err); 
               }); 

          Principle 3: STS Interim Certification

          Bce-bos-uploader supports STS (Security Token Service) temporary authorization. The server generates a set of temporary AK/SK with specific operation permissions and certain timeliness. This set of temporary AK/SK can be exposed to the browser and used directly. The user only needs to set the AK/SK and SessionToken returned by the server to the bos-ak, bos-sk and uptoken parameters corresponding to bce-bos-uploader.

          Please refer to Temporarily Authorized Access#) for introductions regarding STS. Process flow:

          1.Realize Configuration by server-side Nodejs

          2.Obtain temporary AK/SK/SessionToken

          3.Initialize bce-bos-uploader parameter

          Application Server Nodejs Implementation

          var http = require('http');
          var url = require('url');
          var util = require('util');
          
          var STS = require('@baiducloud/sdk').STS;
          
          var kCredentials = {
              ak: 'Your AK',
              sk: 'Your SK'
          };
          
          function buildStsResponse() {
              var stsClient = new STS({
                  credentials: kCredentials,
                  region: 'bj'
              });
              return stsClient.getSessionToken(60 * 60 * 24, {
                  accessControlList: [{
                      service: 'bce:bos',
                      resource: ['bce-javascript-sdk-demo-test'],
                      region: '*',
                      effect: 'Allow',
                      permission: ['READ', 'WRITE']
                  }]
              }).then(function (response) {
                  var body = response.body;
                  return {
                      AccessKeyId: body.accessKeyId,
                      SecretAccessKey: body.secretAccessKey,
                      SessionToken: body.sessionToken,
                      Expiration: body.expiration
                  };
              });
          }
          
          http.createServer(function (req, res) {
              console.log(req.url);
          
              var query = url.parse(req.url, true).query;
          
              var promise = null;
          
              if (query.sts) {
                  promise = buildStsResponse();
              }
          
              promise.then(function (payload) {
                  res.writeHead(200, {
                      'Content-Type': 'text/javascript; charset=utf-8',
                      'Access-Control-Allow-Origin': '*'
                  });
          
                  if (query.callback) {
                      res.end(util.format('%s(%s)', query.callback, JSON.stringify(payload)));
                  }
                  else {
                      res.end(JSON.stringify(payload));
                  }
              });
          }).listen(1337);
          console.log('Server running at http://0.0.0.0:1337/');

          On the server, create a stsClient instance in a similar way to creating a bosClient instance. For the stsClient instance, there is mainly one method, which is getSessionToken. In this method, two parameters are accepted. The first parameter is temporarily authorized validity period with second as unit; the second parameter is concrete permission control, referring to STS Server Port.

          This method will asynchronously access the STS authorization server and return a promise object. The STS authorization server will return something similar to the following:

          {   
              body: {         
                  "accessKeyId": "d87a16e5ce1d47c1917b38ed03fbb329", 
                  "secretAccessKey": "e9b6f59ce06c45cdaaea2296111dab46", 
                  "sessionToken": "MjUzZjQzNTY4OTE0NDRkNjg3N2E4YzJhZTc4YmU5ZDh8AAAAABwCAAB/HfHDVV2bu5xUf6rApt2YdSLG6+21UTC62EHvIuiaamtuMQQKNkR9PU2NJGVbuWgBn8Ot0atk0HnWYQGgwgyew24HtbrX3GFiR/cDymCowm0TI6OGq7k8pGuBiCczT8qZcarH7VdZBd1lkpYaXbtP7wQJqiochDXrswrCd+J/I2CeSQT6mJiMmvupUV06R89dWBL/Vcu7JQpdYBk0d5cp2B+gdaHddBobevlBmKQw50/oOykJIuho4Wn7FgOGPMPdod0Pf0s7lW/HgSnPOjZCgRl0pihs197rP3GWpnlJRyfdCY0g0GFG6T0/FsqDbxbi8lWzF1QRTmJzzh2Tax8xoPFKGMbpntp//vGP7oPYK1JoES34TjcdcZnLzIRnVIGaZAzmZMUhPEXE5RVX1w8jPEXMJJHSrFs3lJe13o9Dwg==",         
                  "createTime": "2016-02-16T14:01:29Z",         
                  "expiration": "2016-02-16T15:41:29Z",         
                  "userId": "5e433c4a8fe74765a7ec6fc147e25c80"     
              } 
          } 

          The server needs to send the accessKeyId, secretAccessKey and sessionToken fields to the browser.

          Realization of Front End for the Browser

          When front end is subject to temporary authorization mechanism, it is only needed to introduce above parameters accessKeyId, secretAccessKey and secretAccessKey during initialization of all services. Take BOS as an example:

          var bosConfig = { 
               credentials: { 
                   ak: '{accessKeyId}', // Temporary ak issued by STS server 
                   sk: '{secretAccessKey}' // Temporary sk issued by STS server 
               }, 
               sessionToken: '{sessionToken}',  // Temporary sessionToken issued by STS server 
               endpoint: 'http://bj.bcebos.com' 
           }; 
          var client = new baidubce.sdk.BosClient(bosConfig);
          Previous
          Acceleration of BOS by CDN
          Next
          Client Encryption Practice