문제

I use Filepicker to "read" then "store" an image from clients' computer. Now I want to resize the image using Filepicker but always get a 403 error:

POST https://www.filepicker.io/api/file/w11b6aScR1WRXKFbcXON/convert?_cacheBust=1380818787693 403 (FORBIDDEN) 

I am using the same security policy and signature for the "read", "store", and "convert" calls. Is this wrong? Because when "read" and "store" are called there is no file handle yet (e.g. the last string part in InkBlob.url). But it seems the "convert" policy/signature must be generated using the file handle returned with the "store" InkBlob? And if this is the case, what's a more convenient way to do in javascript? Because in "convert" I have no access to the Python function that generates security policies unless I write an API call for that.

My code snippet as below (initialFpSecurityObj was pre-generated in Python using an empty handle):

filepicker.store(thumbFile, {
    policy: initialFpSecurityObj.policy, 
    signature: initialFpSecurityObj.signature,
    location: "S3",
    path: 'thumbs/' + initialFpSecurityObj.uniqueName + '/',
},function(InkBlob){
    console.log("Store successful:", JSON.stringify(InkBlob));
    processThumb(InkBlob);
}, function(FPError){
    console.error(FPError.toString());
});

var processThumb = function(InkBlob){
    filepicker.convert(InkBlob, {
        width: 800,
        height: 600,
        format: "jpg",
        policy: initialFpSecurityObj.policy, 
        signature: initialFpSecurityObj.signature,
    }, function(InkBlob){
        console.log("thumbnail converted and stored at:", InkBlob);
    }, function(FPError){
        console.error(FPError);
    };
}

Thanks a lot for the help.

--- EDIT ---

Below is the snippet for the Python code that generates initialFpSecurityObj

def generateFpSecurityOptions(handle, userId, policyLife=DEFAULT_POLICY_LIFE):
    expiry = int(time() + policyLife)
    json_policy = json.dumps({'handle': handle, 'expiry': expiry})
    policy = base64.urlsafe_b64encode(json_policy)

    secret = 'XXXXXXXXXXXXXX'
    signature = hmac.new(secret, policy, hashlib.sha256).hexdigest()

    uniqueName = hashlib.md5()
    uniqueName.update(signature + repr(time()))
    uniqueName = uniqueName.hexdigest() + str(userId)

    return {'policy':policy, 'signature':signature, 'expiry':expiry, 'uniqueName':uniqueName}

fp_security_options = generateFpSecurityOptions(None, request.user.id)

Then in the django template fp_security_options is retrieved:

var initialFpSecurityObj = {{fp_security_options|as_json|safe}};

The way that generates fp_security_options is suspicious to me (former colleague's code) because the handle is None.

도움이 되었습니까?

해결책 3

So I finally figured it out myself, although I saw brettcvz's suggestion afterwards. The key is for 'convert' to work, I have to specify the exact handle of the uploaded file (i.e. the last bit of the string in InkBlob's url property returned from the 'store' or 'pickAndStore' call.

First thing I did was to edit the Python function generating the security policy and signature:

def generateFpSecurityOptions(handle, userId, policyLife=DEFAULT_POLICY_LIFE):
    expiry = int(time() + policyLife)
    json_policy = json.dumps({'handle': handle, 'expiry': expiry})
    policy = base64.urlsafe_b64encode(json_policy)

    secret = 'XXXXXXXXXXXXXX'
    signature = hmac.new(secret, policy, hashlib.sha256).hexdigest()

    if not handle == None:
        uniqueName = handle
    else:
        uniqueName = hashlib.md5()
        uniqueName.update(signature + repr(time()))
        uniqueName = uniqueName.hexdigest() + str(userId)

    return {'policy':policy, 'signature':signature, 'expiry':expiry, 'uniqueName':uniqueName}

fp_security_options = generateFpSecurityOptions(None, request.user.id)

Then I have to established the API call in our Django framework to get this security policy object dynamically via AJAX. I am fortunate that my colleague has previously written it. So I just call the API function in Javascript to retrieve the file-specific security policy object:

var initialFpSecurityObj = {{fp_security_options|as_json|safe}};
filepicker.store(thumbFile, {
    policy: initialFpSecurityObj.policy, 
    signature: initialFpSecurityObj.signature,
    access: "public"
}, function(InkBlob) {
    processThumb(InkBlob);
}, function(FPError) {
    console.error(FPError.toString());
}, function(progress) {
    console.log("Loading: " + progress + "%");
});

var processThumb = function(InkBlob) {
    var fpHandle = InkBlob.url.split('/').pop();
    $.ajax({
        url: API_BASE + 'file_picker_policy',
        type: 'GET',
        data: {
            'filename': fpHandle
        },
        dataType: 'json',
        success: function(data) {
            var newFpSecurityObj = data.data;
        filepicker.convert(InkBlob, {
            width: 800,
            height: 600,
            format: "jpg",
            policy: newFpSecurityObj.policy, 
            signature: newFpSecurityObj.signature,
        }, {
            location: "S3",
            path: THUMB_FOLDER + '/' + newFpSecurityObj.uniqueName + '/', 
        }, function(fp) { // onSuccess
            console.log("successfully converted and stored!");
            // do what you want with the converted file
        }, function(FPError) { // onError
            console.error(FPError);
        }); 
        },
        failure: function() {
        alert("There was an error converting the thumbnail! Please try again.");
        }
    });

};

다른 팁

My recommendation would be to create two policies: one that is handle-bound and allows storing of the file, and another that is not handle-bound for the convert. In this case, you can set a shorter expiry time to increase the level of security, given that you are not specifying a handle.

Your problem is probably that your policy does not contain any "call" specifications. I suggest:

json_policy = json.dumps({'handle': handle, 'expiry': expiry, 'call':['pick','store','read','convert']})

but as our (very busy ;) brettcvz suggests, for conversion only, this is already enough:

json_policy = json.dumps({'handle': handle, 'expiry': expiry, 'call':'convert'})

You can find this in the security docs https://developers.inkfilepicker.com/docs/security/

If you still have issues, use a REST call, it's free. The following method is JavaScript and returns an url to the REST endpoint of filepicker which can be used to retrieve the converted image. The _options object looks like this

var myOptions = {
    w: 150,
    h: 150,
    fit: "crop",
    align: "faces",
    format: "jpg",
    quality: 86
};

and will work with all parameters specified of file pickers REST-API (check out https://developers.inkfilepicker.com/docs/web/#inkblob-images).

function getConvertedURL(_handle, _options, _policy, _signature) {
    // basic url piece
    var url = "https://www.filepicker.io/api/file/" + _handle + "/convert?";
    // appending options
    for (var option in _options) {
        if (_options.hasOwnProperty(option)) {
            url += option + "=" + _options[option] + "&";
        }
    }
    // appending signed policy
    url += "signature=" + _signature + "&policy=" + _policy;
    return url;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top