Updated on 2024-01-15 GMT+08:00

Using a Presigned URL to Upload Data to OBS

Solution Architecture

Each request initiated by an app client applies for a presigned URL from the app server. The validity period of the presigned URL is determined by the app server. Figure 1 describes the process.

Figure 1 Process for a mobile app to access data in OBS

Role Analysis

  • App client: End user's mobile app. It requests a presigned URL from the app server, and uploads data to or downloads data from OBS.
  • App server: A backend provided by developers of Android or iOS apps. It manages the credential information and issues presigned URLs.
  • OBS: Huawei Cloud's object storage service. It processes requests from mobile apps.
Workflow
  1. An app client requests a presigned URL from the app server.

    Access keys (AK and SK) are not required for accessing OBS from Android or iOS apps. But a presigned URL must be obtained from the app server before accessing OBS, and required information must be carried in the URL, including the request type, resource path, and resource name. For example, an upload request needs to indicate that the URL is for uploading data. In the URL, the upload path and object name are specified. Similarly, a URL for downloading data should contain the name of the object to be downloaded.

  2. As a trusted device, the application server stores access keys (AK and SK). After verifying that the client is valid, the app server generates a presigned URL using the stored access keys (AK and SK), in accordance with the operation type and resources to be accessed by the client.
  3. Android/iOS mobile apps obtain the URL and use the URL to perform desired operations, such as uploading and downloading data.

    The URL contains the access key ID (AK) of the user, signature, validity period, and resource information. Anyone who has the URL can perform the operation. After receiving the request and verifying the signature, OBS deems that the request is executed by the user who issues the URL. For example, you can construct an object download URL with signature information, but the URL is valid only within the expiration time specified by Expires. If temporary access keys are used, the URL validity period is the smaller value of either the expiration time specified by Expires or the validity period of the temporary access keys. The URL that carries the signature is used to allow others to use the presigned URL for identity authentication when the SK is not provided, and perform the predefined operation.

Prerequisites

  • A bucket has been created.

    Create a bucket on OBS Console. Configure the bucket permissions, and allow it to be read/written privately, read publicly, or written privately.

    For details, see Creating a Bucket and Configuring a Custom Bucket Policy.

  • Access keys (AK and SK) have been obtained.

    The presigned URL is generated using the access keys. For details about how to obtain access keys, see Access Keys (AK/SK). The user who uses the access keys (AK/SK) needs to have the minimum required permissions. For details about how to authorize the permissions, see Creating a User and Granting OBS Permissions.

Resource and Cost Planning

The table below describes the resources that you need in this practice.

Table 1 Resource description

Resource

Description

App client

End user's mobile app. It requests a presigned URL from the app server, and uploads data to or downloads data from OBS.

App server

A backend provided by developers of Android or iOS apps. It manages the credential information and issues presigned URLs.

OBS

Huawei Cloud's object storage service that processes requests from mobile apps.

Procedure

  1. Configure an app server.

    1. Obtain the SDK.
    2. Generate the code for issuing a presigned URL.

      For details, see Authentication of Signature in a URL.

      The following example describes how to use Java for development on the app server.

      The application server needs to identify the common request header and user-defined request header based on the operation type initiated by the app, and add the headers to the presigned URL for computing the signature.

      • For details about common request headers, see Constructing a Request.
      • For details about user-defined request headers, see the corresponding operation in the API Reference. For example, for PUT uploads, see Uploading Objects - PUT.
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      // Endpoint of the requested bucket
      String endPoint = "http://your-endpoint"; 
      
      // Hard-coded or plaintext AK and SK are risky. For security purposes, encrypt your AK and SK and store them in the configuration file or environment variables. In this example, the AK and SK are stored in environment variables for identity authentication. Before running the code in this example, configure environment variables ACCESS_KEY_ID and SECRET_ACCESS_KEY_ID.
      // Obtain an AK and SK pair on the management console. For details, see https://support.huaweicloud.com/eu/usermanual-ca/ca_01_0003.html.
      String ak = System.getenv("ACCESS_KEY_ID");
      String sk = System.getenv("SECRET_ACCESS_KEY_ID");
      
      //Create an ObsClient.
      ObsClient obsClient = new ObsClient(ak, sk, endPoint); 
      //Define the expiration time, in seconds.
      long expireSeconds = 3600L; 
      
      //Specify the requested operation.
      TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.PUT, expireSeconds); 
      
      //Specify the bucket name and object name involved in this operation.
      request.setBucketName("bucketname"); 
      request.setObjectKey("objectname"); 
      
      TemporarySignatureResponse response = obsClient.createTemporarySignature(request); 
      
      //If the following message is returned, the presigned URL is successfully issued, and you can print the URL information.
      System.out.println(response.getSignedUrl());
      

  2. Use the presigned URL to initiate an OBS access request.

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    public class Demo extends Activity
    {
        private static String bucketName = "my-obs-bucket-demo";
        private static String objectKey = "my-obs-object-key-demo";
        private static OkHttpClient httpClient;
        private static StringBuffer sb;
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            sb = new StringBuffer();
            /*
            * Constructs a client instance with your account for accessing OBS
            */
            httpClient = new OkHttpClient.Builder().followRedirects(false).retryOnConnectionFailure(false)
                  .cache(null).build();
            final TextView tv = (TextView)findViewById(R.id.tv);
            tv.setText("Click to start test");
            tv.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    tv.setClickable(false);
                    AsyncTask<Void, Void, String> task = new DownloadTask();
                    task.execute();
                }
            });
        }
    
        class DownloadTask extends AsyncTask<Void, Void, String>
        {
            @Override
            protected String doInBackground(Void... params)
            {
                try
                {
                    /*
                      * You need to construct an object upload request and send it to the application server to generate a presigned URL for accessing OBS.
                      *  If the response result is stored in response, obtain the URL using the getSignedUrl() method.
                     */
                    sb.append("Uploading a new object to OBS from a file\n\n");
                    Request.Builder builder = new Request.Builder();
    //Make a PUT request to upload an object.
                    Request httpRequest = builder.url(response.getSignedUrl()).put(RequestBody.create(MediaType.parse(contentType), "Hello OBS".getBytes("UTF-8"))).build();
                    Call c = httpClient.newCall(httpRequest);
                    Response res = c.execute();
                    sb.append("\tStatus:" + res.code());
                    if (res.body() != null) {
                           sb.append("\tContent:" + res.body().string() + "\n");
                    }
                    res.close();
    
                    /*
                      * You need to construct an object download request and send it to the application server to generate a presigned URL for accessing OBS.
                      *  If the response result is stored in response, obtain the URL using the getSignedUrl() method.
                     */
                    sb.append("Downloading an object\n\n");
                    Request.Builder builder = new Request.Builder();
    //Make a GET request to download an object.
                    Request httpRequest = builder.url(response.getSignedUrl()).get().build();
                    OkHttpClient httpClient = new OkHttpClient.Builder().followRedirects(false).retryOnConnectionFailure(false).cache(null).build();
                    Call c = httpClient.newCall(httpRequest);
                    Response res = c.execute();
                    System.out.println("\tStatus:" + res.code());
                    if (res.body() != null) {
                           sb.append("\tContent:" + res.body().string() + "\n");
                    }
                    res.close();
    
                    return sb.toString();
                }
                catch (Exception e)
                {
                    sb.append("\n\n");
                    sb.append(e.getMessage());
                    return sb.toString();
                }
                finally
                {
                    if (httpClient != null)
                    {
                        try
                        {
                            /*
                             * Close obs client
                             */
                            httpClient.close();
                        }
                        catch (IOException e)
                        {
                        }
                    }
                }
            }
    
            @Override
            protected void onPostExecute(String result)
            {
                TextView tv = (TextView)findViewById(R.id.tv);
                tv.setText(result);
                tv.setOnClickListener(null);
                tv.setMovementMethod(ScrollingMovementMethod.getInstance());
            }
        }
    }