This topic describes how to calculate signatures in Java on the server, configure upload callbacks, and then use form upload to upload data to Object Storage Service (OSS).

Prerequisites

  • You can access the domain name of the application server over the Internet.
  • Java 1.6 or later is installed on the application server. To view the Java version, run the java -version command.
  • Make sure that the browser that is installed on your on-premises device supports JavaScript.

Step 1: Configure the application server

  1. Download the application server source code package that is in Java.
  2. In this example, Ubuntu 16.04 is used. Extract the downloaded package to the /home/aliyun/aliyun-oss-appserver-java directory.
  3. Go to the directory, open the CallbackServer.java source code file, and modify the source code file based on your business requirements. The following code provides an example on how to modify the source code file:
    // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to perform operations in OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
    String accessId = "yourAccessKeyId";      
    String accessKey = "yourAccessKeySecret"; 
    // In this example, the endpoint of the China (Hangzhou) region is used. Specify your actual endpoint. 
    String endpoint = "oss-cn-hangzhou.aliyuncs.com"; 
    // Specify the name of the bucket. Example: examplebucket. 
    String bucket = "examplebucket"; 
    // Specify the IP address of the host that you want to access in the https://bucketname.endpoint format.                    
    String host = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com"; 
    // Specify the URL of the application server to which an upload callback request is sent. This URL is used for communication between the application server and OSS. After you upload an object, OSS uses the URL to send information about object upload to the application server. 
    String callbackUrl = "https://192.168.0.0:8888";
    // Specify the prefix of the directory in which you want to store the objects that are uploaded to OSS. You can also leave this parameter empty. If you do not specify the prefix, the objects are uploaded to the root directory of the bucket. 
    String dir = "exampledir/"; 

Step 2: Configure the client

  1. Download the client source code package.
  2. Extract the package to a directory. In this example, the D:\aliyun\aliyun-oss-appserver-js directory is used.
  3. Go to the directory, open the upload.js file, and find the following code:
    // serverUrl specifies the URL of the application server that returns information such as the signature and upload policies. Replace the value of serverUrl with the actual IP address and port number of the application server. 
    serverUrl = 'http://192.0.2.0:8888'
  4. Set severUrl to the URL of the application server that returns information such as the signature and upload policies to the client. In this example, serverUrl is set to https://192.168.0.0:8888.

Step 3: Configure CORS

When you use form upload to upload data from the client to OSS, the client sends a request that contains the Origin header to OSS from the browser. Then, OSS checks whether the request that contains the Origin header matches the cross-origin resource sharing (CORS) rules that you configured for a specific bucket. Therefore, you must configure CORS rules for the bucket before users use the POST method to upload data to the bucket.

  1. Log on to the OSS console.
  2. In the left-side navigation pane, click Buckets. On the Buckets page, click the name of the desired bucket.
  3. In the left-side navigation pane, choose Access Control > Cross-Origin Resource Sharing (CORS). In the Cross-Origin Resource Sharing (CORS) section, click Configure.
  4. Click Create Rule and configure the parameters as shown in the following figure.
    Note To ensure data security, we recommend that you specify the domain name from which you want OSS to allow requests in Sources. For more information about how to configure the parameters, see Configure CORS.

Step 4: Send an upload callback request

  1. Start the application server.
    In the /home/aliyun/aliyun-oss-appserver-java directory, run the mvn package command to compile and package the code. Then, run the java -jar target/appservermaven-1.0.0.jar 1234 command to start the application server.
    Note Replace the IP address and port number with the IP address and port number of the application server that you configured.
    You also can use an integrated development environment (IDE), such as Eclipse or IntelliJ IDEA, to export the JAR package on your on-premises device and copy the JAR package to the application server. Then, run the JAR package to start the application server.
  2. Start the client.
    1. On the client that is installed on your on-premises device, open the index.html file in the directory of the client source code.
      Notice The index.html file may be incompatible with Internet Explorer 10 or earlier. If you encounter any problems when you use Internet Explorer 10 or earlier, you must perform debugging.
    2. Click Select File, select a file of the specified type, and then click Upload.
      After you upload the object, the content that is returned by the application server is displayed.

Core code analysis of the application server

The source code of the application server contains the following complete sample code for signature-based upload and upload callback:

package com.aliyun.oss.appservermaven;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.sql.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;

//import org.junit.Assert;
import net.sf.json.JSONObject;

@SuppressWarnings("deprecation")
@WebServlet(asyncSupported = true)
public class CallbackServer extends HttpServlet {
    /**
     *
     */
    private static final long serialVersionUID = 5522372203700422672L;

    /**
     *Configure signature-based upload and upload callback for GET requests.
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to perform operations in OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
        String accessId = "yourAccessKeyId";
        String accessKey = "yourAccessKeySecret";
        // In this example, the endpoint of the China (Hangzhou) region is used. Specify your actual endpoint. 
        String endpoint = "oss-cn-hangzhou.aliyuncs.com";
        // Specify the name of the bucket. Example: examplebucket. 
        String bucket = "examplebucket";
        // Specify the IP address of the host that you want to access in the https://bucketname.endpoint format. 
        String host = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com";
        // Specify the URL of the application server to which an upload callback request is sent. This URL is used for communication between the application server and OSS. After you upload an object, OSS uses the URL to send information about object upload to the application server. 
        String callbackUrl = "https://192.168.0.0:8888";
        // Specify the directory in which you want to store uploaded objects. You can also leave this parameter empty. If you do not specify the prefix, the objects are uploaded to the root directory of the bucket. 
        String dir = "exampledir/";
        
        // Create an OSSClient instance. 
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessId, accessKey);
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            Map<String, String> respMap = new LinkedHashMap<String, String>();
            respMap.put("accessId", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            // respMap.put("expire", formatISO8601Date(expiration));

            JSONObject jasonCallback = new JSONObject();
            jasonCallback.put("callbackUrl", callbackUrl);
            jasonCallback.put("callbackBody",
                    "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
            jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded");
            String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
            respMap.put("callback", base64CallbackBody);

            JSONObject ja1 = JSONObject.fromObject(respMap);
            // System.out.println(ja1.toString());
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "GET, POST");
            response(request, response, ja1.toString());

        } catch (Exception e) {
            // Assert.fail(e.getMessage());
            System.out.println(e.getMessage());
        }
    }

    /**
     * Obtain the public key.
     *
     * @param url
     * @return
     */
    @SuppressWarnings({ "finally" })
    public String executeGet(String url) {
        BufferedReader in = null;

        String content = null;
        try {
            // Specify HttpClient. 
            @SuppressWarnings("resource")
            DefaultHttpClient client = new DefaultHttpClient();
            // Create an instance for the HTTP method. 
            HttpGet request = new HttpGet();
            request.setURI(new URI(url));
            HttpResponse response = client.execute(request);

            in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuffer sb = new StringBuffer("");
            String line = "";
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                sb.append(line + NL);
            }
            in.close();
            content = sb.toString();
        } catch (Exception e) {
        } finally {
            if (in != null) {
                try {
                    in.close();// Disable BufferedReader. 
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return content;
        }
    }

    /**
     * Obtain the body of a POST request.
     *
     * @param is
     * @param contentLen
     * @return
     */
    public String GetPostBody(InputStream is, int contentLen) {
        if (contentLen > 0) {
            int readLen = 0;
            int readLengthThisTime = 0;
            byte[] message = new byte[contentLen];
            try {
                while (readLen != contentLen) {
                    readLengthThisTime = is.read(message, readLen, contentLen - readLen);
                    if (readLengthThisTime == -1) {// Should not happen.
                        break;
                    }
                    readLen += readLengthThisTime;
                }
                return new String(message);
            } catch (IOException e) {
            }
        }
        return "";
    }

    /**
     * Verify the upload callback request.
     *
     * @param request
     * @param ossCallbackBody
     * @return
     * @throws NumberFormatException
     * @throws IOException
     */
    protected boolean VerifyOSSCallbackRequest(HttpServletRequest request, String ossCallbackBody)
            throws NumberFormatException, IOException {
        boolean ret = false;
        String autorizationInput = new String(request.getHeader("Authorization"));
        String pubKeyInput = request.getHeader("x-oss-pub-key-url");
        byte[] authorization = BinaryUtil.fromBase64String(autorizationInput);
        byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
        String pubKeyAddr = new String(pubKey);
        if (!pubKeyAddr.startsWith("http://gosspublic.alicdn.com/")
                && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")) {
            System.out.println("pub key addr must be oss addrss");
            return false;
        }
        String retString = executeGet(pubKeyAddr);
        retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
        retString = retString.replace("-----END PUBLIC KEY-----", "");
        String queryString = request.getQueryString();
        String uri = request.getRequestURI();
        String decodeUri = java.net.URLDecoder.decode(uri, "UTF-8");
        String authStr = decodeUri;
        if (queryString != null && !queryString.equals("")) {
            authStr += "?" + queryString;
        }
        authStr += "\n" + ossCallbackBody;
        ret = doCheck(authStr, authorization, retString);
        return ret;
    }

    /**
     * Configure signature-based upload and upload callback in POST requests.
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String ossCallbackBody = GetPostBody(request.getInputStream(),
                Integer.parseInt(request.getHeader("content-length")));
        boolean ret = VerifyOSSCallbackRequest(request, ossCallbackBody);
        System.out.println("verify result : " + ret);
        // System.out.println("OSS Callback Body:" + ossCallbackBody);
        if (ret) {
            response(request, response, "{\"Status\":\"OK\"}", HttpServletResponse.SC_OK);
        } else {
            response(request, response, "{\"Status\":\"verdify not ok\"}", HttpServletResponse.SC_BAD_REQUEST);
        }
    }

    /**
     * Verify the RSA-based signature.
     *
     * @param content
     * @param sign
     * @param publicKey
     * @return
     */
    public static boolean doCheck(String content, byte[] sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            boolean bverify = signature.verify(sign);
            return bverify;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * The response returned by the application server.
     *
     * @param request
     * @param response
     * @param results
     * @param status
     * @throws IOException
     */
    private void response(HttpServletRequest request, HttpServletResponse response, String results, int status)
            throws IOException {
        String callbackFunName = request.getParameter("callback");
        response.addHeader("Content-Length", String.valueOf(results.length()));
        if (callbackFunName == null || callbackFunName.equalsIgnoreCase(""))
            response.getWriter().println(results);
        else
            response.getWriter().println(callbackFunName + "( " + results + " )");
        response.setStatus(status);
        response.flushBuffer();
    }

    /**
     * The response returned by the application server.
     */
    private void response(HttpServletRequest request, HttpServletResponse response, String results) throws IOException {
        String callbackFunName = request.getParameter("callback");
        if (callbackFunName == null || callbackFunName.equalsIgnoreCase(""))
            response.getWriter().println(results);
        else
            response.getWriter().println(callbackFunName + "( " + results + " )");
        response.setStatus(HttpServletResponse.SC_OK);
        response.flushBuffer();
    }
}           

For more information about the API operation that you can call to configure upload callback, see Callback.