Practice of Direct Upload for Mobile Apps

Last updated: 2023-09-12 18:30:06

Feature Overview

This document explains how to upload files directly to a Cloud Object Storage (COS) bucket from an App without relying on the SDK, using simple code.
Note
The content of this document is based on the XML version of the API.

Preparations

1. Log in to the COS console and create a bucket to obtain the Bucket (bucket name) and Region (region name). For more information, please refer to the Creating Bucket document.
2. Log in to the Access Management Console to obtain your project's SecretId and SecretKey.

Practical Steps

The overall process is as follows:
1. The client calls the server-side interface and passes the file extension. The server then generates a COS key and direct upload URL based on the extension and timestamp.
2. The server obtains temporary keys through the STS SDK.
3. The server uses the obtained temporary key to sign the direct upload URL and returns the URL, signature, token, and other information.
4. After obtaining the information from step 3, the client initiates a PUT request with the signature, token, and other headers for the upload.
For specific code, refer to the iOS example and Android example.

Configure Server-side Implementation for Signature Generation

Note
During the official deployment, please add a layer of your website's own permission verification to the server-side.
For security reasons, the backend obtains a temporary key, generates a direct upload URL, and signs it directly. You can refer to the Server Example for more information. The specific steps are as follows:
1. Obtain temporary keys using the STS SDK.
2. Generate the COS key and direct upload URL based on the file extension.
3. Sign the direct upload URL using a temporary key and return the direct upload URL, signature, token, and other information.
Server-side Configuration Steps:
1. Ensure that the keys, bucket, and region are properly configured.
var config = {
// Obtain Tencent Cloud key, it is recommended to use the key of a sub-user with restricted permissions https://console.cloud.tencent.com/cam/capi
secretId: process.env.COS_SECRET_ID,
secretKey: process.env.COS_SECRET_KEY,
// Key validity period
durationSeconds: 1800,
// Enter the bucket and region here, for example: test-1250000000, ap-guangzhou
bucket: process.env.PERSIST_BUCKET,
region: process.env.PERSIST_BUCKET_REGION,
// Restricted upload extensions
extWhiteList: ['jpg', 'jpeg', 'png', 'gif', 'bmp'],
};
2. Terminal Execution
npm install
3. Enable the service
node app.js
At this point, the server-side has started successfully, and the client-side process can begin.
To use other languages or implement it on your own, please take the following steps:
1. Obtain temporary keys from the server. The server first uses the fixed keys SecretId and SecretKey to request temporary keys from the STS service, obtaining tmpSecretId, tmpSecretKey, and sessionToken. For more information, please refer to the Temporary Key Generation and Usage Guide or the cos-sts-sdk documentation.
2. Sign the direct upload URL to generate an authorization.
3. Return the direct upload URL, authorization, sessionToken, and other information. When the client uploads a file, place the obtained signature and sessionToken in the authorization and x-cos-security-token fields of the request header, respectively.

Client Upload Example

iOS Client Upload

1. Request direct upload and signature information from the server.
The server address here refers to the Node service started in the previous step.
// ext represents the file extension of the file to be uploaded, such as jpg, png, etc.
http://127.0.0.1:3000/sts-direct-sign?ext=jpg
iOS Request Code
NSURL *stsURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:3000/sts-direct-sign?ext=%@",@"file_extension"]];
NSMutableURLRequest * stsRequest = [NSMutableURLRequest requestWithURL:stsURL];
[stsRequest setHTTPMethod:@"GET"];
NSURLSession * session = [NSURLSession sharedSession];
NSURLSessionDataTask * task = [session dataTaskWithRequest:stsRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary * dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSDictionary *params = dic[@"data"]; // This contains the signature information.
}];
[task resume];
2. Begin uploading files using the obtained signature information.
// params is the dic[@"data"] obtained from the previous step request
NSString * cosHost = [params objectForKey:@"cosHost"];
NSString * cosKey = [params objectForKey:@"cosKey"];
NSString * authorization = [params objectForKey:@"authorization"];
NSString * securityToken = [params objectForKey:@"securityToken"];

NSURL * stsURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@/%@",cosHost,cosKey]];
NSMutableURLRequest * stsRequest = [NSMutableURLRequest requestWithURL:stsURL];
[stsRequest setHTTPMethod:@"PUT"];
[stsRequest setAllHTTPHeaderFields:@{
@"Content-Type":@"application/octet-stream",
@"Content-Length":@(data.length).stringValue,// data is the NSData of the file to be uploaded
@"Authorization":authorization,
@"x-cos-security-token":securityToken,
@"Host":cosHost
}];

NSURLSession * session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];

NSURLSessionDataTask * task = [session uploadTaskWithRequest:stsRequest fromData:self.body completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(error){
NSLog(@"Upload failed");
} else {
NSLog(@"Upload successful");
}
}];
[task resume];

Android Client Upload

1. Request direct upload and signature information from the server.
/**
* Obtain the direct upload URL and signature, etc.
*
* @param ext File extension: The backend will generate a COS key based on the extension after direct upload.
* @return Direct upload URL and signature, etc.
*/
private JSONObject getStsDirectSign(String ext) {
// Obtain the upload path and signature
HttpURLConnection getConnection = null;
try {
// Direct upload signature service URL (For production environment, please replace with the official direct upload signature service URL)
URL url = new URL("http://127.0.0.1:3000/sts-direct-sign?ext=" + ext);
getConnection = (HttpURLConnection) url.openConnection();
getConnection.setRequestMethod("GET");

int responseCode = getConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(getConnection.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
reader.close();
JSONObject jsonObject;
try {
jsonObject = new JSONObject(stringBuilder.toString());
if(jsonObject.has("code") && jsonObject.optInt("code") == 0){
return jsonObject.optJSONObject("data");
} else {
Log.e(TAG, String.format("getStsDirectSign error code: %d, error message: %s", jsonObject.optInt("code"), jsonObject.optString("message")));
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
Log.e(TAG, "getStsDirectSign HTTP error code: " + responseCode);
}
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "getStsDirectSign Error sending GET request: " + e.getMessage());
} finally {
if (getConnection != null) {
getConnection.disconnect();
}
}
return null;
}
2. Begin uploading files using the obtained direct upload and signature information.
/**
* Uploading files
* @param filePath Local file path
* @param listener Progress callback
*/
private void uploadFile(String filePath, final ProgressListener listener) {
File file = new File(filePath);
// Obtain direct upload signature and related data
JSONObject directTransferData = getStsDirectSign(FilePathHelper.getFileExtension(file));
if (directTransferData == null) {
toastMessage("getStsDirectSign fail");
return;
}
String cosHost = directTransferData.optString("cosHost");
String cosKey = directTransferData.optString("cosKey");
String authorization = directTransferData.optString("authorization");
String securityToken = directTransferData.optString("securityToken");

// Generate the upload URL
URL url;
try {
url = new URL(String.format("https://%s/%s", cosHost, cosKey));
} catch (MalformedURLException e) {
e.printStackTrace();
return;
}

HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("PUT");
conn.setDoOutput(true);

// Set the request header
conn.setRequestProperty("Content-Type", "application/octet-stream");
conn.setRequestProperty("Content-Length", String.valueOf(file.length()));
conn.setRequestProperty("Authorization", authorization);
conn.setRequestProperty("x-cos-security-token", securityToken);
conn.setRequestProperty("Host", cosHost);

// Obtain the output stream
OutputStream outputStream = conn.getOutputStream();

// Read the file and upload it
FileInputStream inputStream = new FileInputStream(file);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
long totalBytesRead = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
if (listener != null) {
listener.onProgress(totalBytesRead, file.length());
}
}
// Close the stream
inputStream.close();
outputStream.flush();
outputStream.close();

// Obtain the response code
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
// Uploaded successfully
} else {
Log.e(TAG, "uploadFile HTTP error code: " + responseCode);
}
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "uploadFile Error sending PUT request: " + e.getMessage());
} finally {
if (conn != null) {
conn.disconnect();
}
}
}

Documentation

If you have more extensive API usage requirements, please refer to the following client SDK documentation: