tencent cloud

Feedback

Practice of Direct Transfer for Web End

Last updated: 2024-09-29 12:07:08

    Overview

    This document describes how to use simple code to upload files to a COS bucket directly from the web without using an SDK.
    Note:
    This document is based on the XML APIs.

    Prerequisites

    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 see Creating Buckets.
    2. Go to the bucket detail page, choose the Security Management tab, and select CORS (Cross-Origin Resource Sharing) from the drop-down list. Then, click Add a Rule. A configuration example is shown in the following figure. For more information, please see Setting Cross-Origin Access.
    
    
    3. Log in to the CAM console and obtain the SecretId and SecretKey of your project.

    Solution Description

    Implementation Process

    1. Select a file on the front end, and the front end will send the suffix to the server.
    2. The server generates a random COS file path with time according to the suffix, calculates the corresponding signature, and returns the URL and signature information to the front end.
    3. The front end uses a PUT or POST request to directly upload the file to COS.

    Solution Strength

    Permissions security: The scope of security permissions can be effectively limited with the server-side signatures, which can only be used to upload a specified file path.
    Path security: The random COS file path is determined by the server, which can effectively avoid the problem of existing files being overwritten and security risks.

    Practical Steps

    Configuring the Server to Implement Signatures

    Note:
    Add a layer of authority check on your website itself when the server is officially deployed.
    Refer to the document Request Signature for how to calculate the signature.
    Refer to Nodejs Example for the server-side calculation signature code using Nodejs.

    Web Upload Example

    The following code is an example of PUT Object interface (recommended) and POST Object interface, the operation guide is as follows:

    Use AJAX PUT to upload

    AJAX Upload requires the browser to support basic HTML5 features. The current solution uses PUT Object documentation. The operation guide is as follows:
    1. Prepare the bucket configuration according to the steps of Prerequisites.
    2. Create a test.html file and copy the code below into the test.html file.
    3. Deploy the back-end signature service and modify the signature service address in test.html.
    4. Place test.html under the Web server, access the page through a browser, and test the file upload feature.
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <title>Ajax Put Upload (Server-side signature calculation)</title>
    <style>
    h1,
    h2 {
    font-weight: normal;
    }
    
    #msg {
    margin-top: 10px;
    }
    </style>
    </head>
    <body>
    <h1>Ajax Put Upload (Server-side signature calculation)</h1>
    
    <input id="fileSelector" type="file" />
    <input id="submitBtn" type="submit" />
    
    <div id="msg"></div>
    
    <script>
    (function () {
    // url encode format for encoding more characters
    const camSafeUrlEncode = function (str) {
    return encodeURIComponent(str)
    .replace(/!/g, '%21')
    .replace(/'/g, '%27')
    .replace(/\\(/g, '%28')
    .replace(/\\)/g, '%29')
    .replace(/\\*/g, '%2A');
    };
    
    // Calculate the signature.
    const getAuthorization = function (opt, callback) {
    // Replace with your server address to get the PUT upload signature, demo: https://github.com/tencentyun/cos-demo/blob/main/server/upload-sign/nodejs/app.js
    const url = http://127.0.0.1:3000/put-sign?ext=${opt.ext};
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onload = function (e) {
    let credentials;
    try {
    const result = JSON.parse(e.target.responseText);
    credentials = result;
    } catch (e) {
    callback('Error in getting signature');
    }
    if (credentials) {
    // Print to confirm if the credentials are correct
    // console.log(credentials);
    callback(null, {
    securityToken: credentials.sessionToken,
    authorization: credentials.authorization,
    cosKey: credentials.cosKey,
    cosHost: credentials.cosHost,
    });
    } else {
    console.error(xhr.responseText);
    callback('Error in getting signature');
    }
    };
    xhr.onerror = function (e) {
    callback('Error in getting signature');
    };
    xhr.send();
    };
    
    // Uploading Files
    const uploadFile = function (file, callback) {
    const fileName = file.name;
    // Get the file extension
    let ext = '';
    const lastDotIndex = fileName.lastIndexOf('.');
    if (lastDotIndex > -1) {
    // Get the file extension here. The server generates the final upload path.
    ext = fileName.substring(lastDotIndex + 1);
    }
    getAuthorization({ ext }, function (err, info) {
    if (err) {
    alert(err);
    return;
    }
    const auth = info.authorization;
    const securityToken = info.securityToken;
    const Key = info.cosKey;
    const protocol =
    location.protocol === 'https:' ? 'https:' : 'http:';
    const prefix = protocol + '//' + info.cosHost;
    const url =
    prefix + '/' + camSafeUrlEncode(Key).replace(/%2F/g, '/');
    const xhr = new XMLHttpRequest();
    xhr.open('PUT', url, true);
    xhr.setRequestHeader('Authorization', auth);
    securityToken &&
    xhr.setRequestHeader('x-cos-security-token', securityToken);
    xhr.upload.onprogress = function (e) {
    console.log(
    'Upload progress ' +
    Math.round((e.loaded / e.total) * 10000) / 100 +
    '%'
    );
    };
    xhr.onload = function () {
    if (/^2\\d\\d$/.test('' + xhr.status)) {
    const ETag = xhr.getResponseHeader('etag');
    callback(null, { url: url, ETag: ETag });
    } else {
    callback('file' + Key + 'Upload failed, status code: ' + xhr.status);
    }
    };
    xhr.onerror = function () {
    callback(
    'File ' + Key + ' Upload failed. Please check if the CORS cross-domain rules are configured properly'
    );
    };
    xhr.send(file);
    });
    };
    
    // Listen for form submissions
    document.getElementById('submitBtn').onclick = function (e) {
    const file = document.getElementById('fileSelector').files[0];
    if (!file) {
    document.getElementById('msg').innerText = 'No file selected for upload';
    return;
    }
    file &&
    uploadFile(file, function (err, data) {
    console.log(err || data);
    document.getElementById('msg').innerText = err
    ? err
    : 'Upload successfully, ETag=' + data.ETag;
    });
    };
    })();
    </script>
    </body>
    </html>
    The execution effect is as shown below:
    
    

    Use AJAX POST to upload

    AJAX Upload requires the browser to support basic HTML5 features. The current solution uses Post Object interface. Operation guide:
    1. Obtain the bucket information by taking the steps in Prerequisites.
    2. Create a test.html file and copy the code below into the test.html file.
    3. Deploy the signature service at the backend and modify the signature service address in test.html.
    4. Place test.html on the Web server. Then, browser the page to test the file upload feature.
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <title>Ajax Post Upload (Server-side signature calculation)</title>
    <style>
    h1,
    h2 {
    font-weight: normal;
    }
    
    #msg {
    margin-top: 10px;
    }
    </style>
    </head>
    <body>
    <h1>Post Object Upload (Server-side signature calculation)</h1>
    
    <input id="fileSelector" type="file" />
    <input id="submitBtn" type="submit" />
    
    <div id="msg"></div>
    
    <script>
    (function () {
    let prefix = '';
    let Key = '';
    
    // url encode format for encoding more characters
    const camSafeUrlEncode = function (str) {
    return encodeURIComponent(str)
    .replace(/!/g, '%21')
    .replace(/'/g, '%27')
    .replace(/\\(/g, '%28')
    .replace(/\\)/g, '%29')
    .replace(/\\*/g, '%2A');
    };
    
    // Get permission policies
    const getAuthorization = function (opt, callback) {
    // Replace with your server address to get the post upload signature, demo: https://github.com/tencentyun/cos-demo/blob/main/server/upload-sign/nodejs/app.js
    const url = http://127.0.0.1:3000/post-policy?ext=${opt.ext};
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onload = function (e) {
    let credentials;
    try {
    const result = JSON.parse(e.target.responseText);
    credentials = result;
    } catch (e) {
    callback('Error in getting signature');
    }
    if (credentials) {
    // Print to confirm if the credentials are correct
    // console.log(credentials);
    callback(null, {
    securityToken: credentials.sessionToken,
    cosKey: credentials.cosKey,
    cosHost: credentials.cosHost,
    policy: credentials.policy,
    qAk: credentials.qAk,
    qKeyTime: credentials.qKeyTime,
    qSignAlgorithm: credentials.qSignAlgorithm,
    qSignature: credentials.qSignature,
    });
    } else {
    console.error(xhr.responseText);
    callback('Error in getting signature');
    }
    };
    xhr.send();
    };
    
    // Uploading Files
    const uploadFile = function (file, callback) {
    const fileName = file.name;
    // Get the file extension
    let ext = '';
    const lastDotIndex = fileName.lastIndexOf('.');
    if (lastDotIndex > -1) {
    // Get the file extension here. The server generates the final upload path.
    ext = fileName.substring(lastDotIndex + 1);
    }
    getAuthorization({ ext }, function (err, credentials) {
    if (err) {
    alert(err);
    return;
    }
    const protocol =
    location.protocol === 'https:' ? 'https:' : 'http:';
    prefix = protocol + '//' + credentials.cosHost;
    Key = credentials.cosKey;
    const fd = new FormData();
    
    // Put an empty.html in the current directory so that the API can jump back after uploading
    fd.append('key', Key);
    
    // Use policy signature protection formats
    credentials.securityToken &&
    fd.append('x-cos-security-token', credentials.securityToken);
    fd.append('q-sign-algorithm', credentials.qSignAlgorithm);
    fd.append('q-ak', credentials.qAk);
    fd.append('q-key-time', credentials.qKeyTime);
    fd.append('q-signature', credentials.qSignature);
    fd.append('policy', credentials.policy);
    
    // File content, place the file field at the end of the form to avoid long file content affecting the signature judgment and authentication.
    fd.append('file', file);
    
    // xhr
    const url = prefix;
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    xhr.upload.onprogress = function (e) {
    console.log(
    'Upload progress ' +
    Math.round((e.loaded / e.total) * 10000) / 100 +
    '%'
    );
    };
    xhr.onload = function () {
    if (Math.floor(xhr.status / 100) === 2) {
    const ETag = xhr.getResponseHeader('etag');
    callback(null, {
    url:
    prefix + '/' + camSafeUrlEncode(Key).replace(/%2F/g, '/'),
    ETag: ETag,
    });
    } else {
    callback('file' + Key + 'Upload failed, status code: ' + xhr.status);
    }
    };
    xhr.onerror = function () {
    callback(
    'File ' + Key + ' Upload failed. Please check if the CORS cross-domain rules are configured properly'
    );
    };
    xhr.send(fd);
    });
    };
    
    // Listen for form submissions
    document.getElementById('submitBtn').onclick = function (e) {
    const file = document.getElementById('fileSelector').files[0];
    if (!file) {
    document.getElementById('msg').innerText = 'No file selected for upload';
    return;
    }
    file &&
    uploadFile(file, function (err, data) {
    console.log(err || data);
    document.getElementById('msg').innerText = err
    ? err
    : 'Upload successfully, ETag=' + data.ETag + 'url=' + data.url;
    });
    };
    })();
    </script>
    </body>
    </html>
    The execution effect is as shown below:
    
    

    Use Form to upload

    Form Upload supports uploads from older browsers (for example, IE8). The current solution uses Post Object interface. Operation guide:
    1. Obtain the bucket information by taking the steps in Prerequisites.
    2. Create a test.html file and copy the code below into the test.html file.
    3. Deploy the signature service at the backend and modify the signature service address in test.html.
    4. In the directory where test.html is stored, create an empty empty.html file to be redirected back when the upload is successful.
    5. Place test.html and empty.html on the Web server. Then, browse the page to test the file upload feature.
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <title>Simple Form Upload (Compatible with IE8) (Server-side Signature Calculation)</title>
    <style>
    h1,
    h2 {
    font-weight: normal;
    }
    #msg {
    margin-top: 10px;
    }
    </style>
    </head>
    <body>
    <h1>Simple Form Upload (Compatible with IE8) (Server-side Signature Calculation)</h1>
    <div>Minimum compatibility with IE6 for uploading, and onprogress is not supported</div>.
    
    <form
    id="form"
    target="submitTarget"
    action=""
    method="post"
    enctype="multipart/form-data"
    accept="*/*"
    >
    <input id="name" name="name" type="hidden" value="" />
    <input name="success_action_status" type="hidden" value="200" />
    <input
    id="success_action_redirect"
    name="success_action_redirect"
    type="hidden"
    value=""
    />
    <input id="key" name="key" type="hidden" value="" />
    <input id="policy" name="policy" type="hidden" value="" />
    <input
    id="q-sign-algorithm"
    name="q-sign-algorithm"
    type="hidden"
    value=""
    />
    <input id="q-ak" name="q-ak" type="hidden" value="" />
    <input id="q-key-time" name="q-key-time" type="hidden" value="" />
    <input id="q-signature" name="q-signature" type="hidden" value="" />
    <input name="Content-Type" type="hidden" value="" />
    <input
    id="x-cos-security-token"
    name="x-cos-security-token"
    type="hidden"
    value=""
    />
    
    <!-- Place the file field at the end of the form to avoid long file content affecting signature judgment and authentication -->
    <input id="fileSelector" name="file" type="file" />
    <input id="submitBtn" type="button" value="Submit" />
    </form>
    <iframe
    id="submitTarget"
    name="submitTarget"
    style="display: none"
    frameborder="0"
    ></iframe>
    
    <div id="msg"></div>
    
    <script>
    (function () {
    const form = document.getElementById('form');
    let prefix = '';
    
    // url encode format for encoding more characters
    const camSafeUrlEncode = function (str) {
    return encodeURIComponent(str)
    .replace(/!/g, '%21')
    .replace(/'/g, '%27')
    .replace(/\\(/g, '%28')
    .replace(/\\)/g, '%29')
    .replace(/\\*/g, '%2A');
    };
    
    // Calculate the signature.
    const getAuthorization = function (opt, callback) {
    // Replace with your server address to get the post upload signature, demo: https://github.com/tencentyun/cos-demo/blob/main/server/upload-sign/nodejs/app.js
    const url = http://127.0.0.1:3000/post-policy?ext=${opt.ext || ''};
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onload = function (e) {
    let credentials;
    try {
    const result = JSON.parse(e.target.responseText);
    credentials = result;
    } catch (e) {
    callback('Error in getting signature');
    }
    if (credentials) {
    // Print to confirm if the credentials are correct
    // console.log(credentials);
    callback(null, {
    securityToken: credentials.sessionToken,
    cosKey: credentials.cosKey,
    cosHost: credentials.cosHost,
    policy: credentials.policy,
    qAk: credentials.qAk,
    qKeyTime: credentials.qKeyTime,
    qSignAlgorithm: credentials.qSignAlgorithm,
    qSignature: credentials.qSignature,
    });
    } else {
    console.error(xhr.responseText);
    callback('Error in getting signature');
    }
    };
    xhr.send();
    };
    
    // Listen upload completion
    let Key;
    const submitTarget = document.getElementById('submitTarget');
    const showMessage = function (err, data) {
    console.log(err || data);
    document.getElementById('msg').innerText = err
    ? err
    : 'Upload successfully, ETag=' + data.ETag;
    };
    submitTarget.onload = function () {
    let search;
    try {
    search = submitTarget.contentWindow.location.search.substr(1);
    } catch (e) {
    showMessage('file' + Key + 'Upload failed');
    }
    if (search) {
    const items = search.split('&');
    let i = 0;
    let arr = [];
    const data = {};
    for (i = 0; i < items.length; i++) {
    arr = items[i].split('=');
    data[arr[0]] = decodeURIComponent(arr[1] || '');
    }
    showMessage(null, {
    url: prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/'),
    ETag: data.etag,
    });
    } else {
    }
    };
    
    // Start uploading
    document.getElementById('submitBtn').onclick = function (e) {
    const filePath = document.getElementById('fileSelector').value;
    if (!filePath) {
    document.getElementById('msg').innerText = 'No file selected for upload';
    return;
    }
    // Get the file extension
    let ext = '';
    const lastDotIndex = filePath.lastIndexOf('.');
    if (lastDotIndex > -1) {
    // Get the file extension here. The server generates the final upload path.
    ext = filePath.substring(lastDotIndex + 1);
    }
    getAuthorization({ ext }, function (err, AuthData) {
    if (err) {
    alert(err);
    return;
    }
    const protocol =
    location.protocol === 'https:' ? 'https:' : 'http:';
    prefix = protocol + '//' + AuthData.cosHost;
    form.action = prefix;
    Key = AuthData.cosKey;
    // Put an empty.html in the current directory so that the API can jump back after uploading
    document.getElementById('success_action_redirect').value =
    location.href.substr(0, location.href.lastIndexOf('/') + 1) +
    'empty.html';
    document.getElementById('key').value = AuthData.cosKey;
    document.getElementById('policy').value = AuthData.policy;
    document.getElementById('q-sign-algorithm').value =
    AuthData.qSignAlgorithm;
    document.getElementById('q-ak').value = AuthData.qAk;
    document.getElementById('q-key-time').value = AuthData.qKeyTime;
    document.getElementById('q-signature').value = AuthData.qSignature;
    document.getElementById('x-cos-security-token').value =
    AuthData.securityToken || '';
    form.submit();
    });
    };
    })();
    </script>
    </body>
    </html>
    The execution effect is as shown below:
    
    

    Restricting the File Type and the File Size During Upload

    File Types Limited by Front End
    Refer to the above AJAX PUT upload, add a layer of judgment when selecting files. (Only restricted file extensions are supported)
    // Omit other codes
    document.getElementById('submitBtn').onclick = function (e) {
    const file = document.getElementById('fileSelector').files[0];
    if (!file) {
    document.getElementById('msg').innerText = 'No file selected for upload';
    return;
    }
    // Get the file extension
    const fileName = file.name;
    const lastDotIndex = fileName.lastIndexOf('.');
    const ext = lastDotIndex > -1 ? fileName.substring(lastDotIndex + 1) : '';
    
    // Please replace with the formats you want to restrict, for example, only jpg and png files
    const allowExt = ['jpg', 'png'];
    if (!allowExt.includes(ext)) {
    alert('Only jpg and png files are supported for upload');
    return;
    }
    
    file &&
    uploadFile(file, function (err, data) {
    console.log(err || data);
    document.getElementById('msg').innerText = err
    ? err
    : 'Upload successfully, ETag=' + data.ETag + 'url=' + data.url;
    });
    };
    File Size Limited by Front End
    Refer to the above AJAX PUT upload, add a layer of judgment when selecting files.
    // Omit other codes
    document.getElementById('submitBtn').onclick = function (e) {
    const file = document.getElementById('fileSelector').files[0];
    if (!file) {
    document.getElementById('msg').innerText = 'No file selected for upload';
    return;
    }
    const fileSize = file.size;
    
    // Please replace with the object size you want to restrict, up to a maximum of 5GB per object. For example, restrict uploaded files to no more than 5MB.
    if (fileSize > 5 * 1024 * 1024) {
    alert('The selected file exceeds 5MB, please selected another one');
    return;
    }
    
    file &&
    uploadFile(file, function (err, data) {
    console.log(err || data);
    document.getElementById('msg').innerText = err
    ? err
    : 'Upload successfully, ETag=' + data.ETag + 'url=' + data.url;
    });
    };
    Server-Side Signature Restriction
    Refer to the server-side signature code Nodejs Example for put-sign-limit and post-policy-limit.

    Reference

    If you need to call more APIs, please see the following JavaScript SDK document:
    
    Contact Us

    Contact our sales team or business advisors to help your business.

    Technical Support

    Open a ticket if you're looking for further assistance. Our Ticket is 7x24 avaliable.

    7x24 Phone Support