In fine uploader, wie bearbeitet / aktualisiert man S3-Dateimetadaten für Dateien, die in früheren Sitzungen hochgeladen wurden?

Ich habe Anforderungen, in denen Benutzer die Metadaten der S3-Datei bearbeiten / aktualisieren müssen, die in den vorherigen Sitzungen hochgeladen wurden. Ich habe die anfängliche Dateiliste implementiert, muss aber die Dateimetadaten (Dateiname, Beschriftung - in meinem Fall neues Feld) in der Anzeigeliste bearbeitbar machen. Kann es erreicht werden?

Ahaedit files feature, aber das ist begrenzt auf, bevor die Datei hochgeladen wird. Sieht aus wie meine Anforderung nicht einfach aus der Box FU unterstützt. Ich habe unten Ansatz gefolgt.

In der Vorlage habe ich eine Schaltfläche mit dem Text 'Update Caption' (Titel aktualisieren), die mit einem Klick auf "captionUpdate ()" die JS-Variable (isCaptionUpdate) auf true setzt.ei der @ Caption-Aktualisierung wird der DeleteFile-Endpunkt ausgelöst, mit der Ausnahme, dass die Parameterdaten für den Beschriftungswert aus dem Textfeld festgelegt werden, das in der Vorlage @ definiert is Im serverseitigen Code prüft der Prozess, ob der Parameter Caption vorhanden ist, und ruft dann die Funktion updateObjectWithCaption () auf.

Alles oben Genannte funktioniert nahtlos mit folgenden Herausforderungen.Bitte sehen Sie sich ... anBildschirmfot.

Wenn der Benutzer auf "Beschriftung aktualisieren" klickt, werden die LÖSCHEN-Schritte ausgeführt, und da ich den Parameter "Beschriftung" übergebe, wird die S3-Datei aktualisiert. Aber das Problem liegt in der Dateiliste. Es wird für kurze Zeit ein Statustext namens "Löschen ....." angezeigt. Wie kann ich den Status in "Untertitel aktualisieren ..." oder in ein ähnliches @ änder Ein weiteres Problem mit Nummer 1 ist, dass die Liste "Datei in Datei" entfernt wird, sobald S3 aktualisiert wird. Der UI-Teil denkt immer noch, dass es sich aus irgendeinem Grund um einen DELETE-Schritt handelt. Wie kann ich der UI mitteilen, dass er nicht wirklich gelöscht wird? Wie Sie im Abschnitt deleteFile von JS sehen können, stammt die Beschriftung aus document.getElementById ('caption'). Value; Das heißt, auch wenn ich auf "Beschriftung aktualisieren" für die 2., 3. oder 4. Datei klicke, tritt das Beschriftungselement zum ersten Mal auf. Wie kann ich die Beschriftung der spezifischen Datei erhalten?Last but not least, wie kann ich die Schaltfläche "Update Caption" nur für zuvor hochgeladene Dateien anzeigen. Ich möchte diese Schaltfläche beim erneuten Hochladen nicht anzeigen.

Sorry für zu viele Fragen. Ich konnte diese Frage nicht trennen, da sie alle mit dem Thema zur Aktualisierung der S3-Dateimetadaten zusammenhängen.

Vorlage

<div class="qq-uploader-selector qq-uploader" qq-drop-area-text="Drop files here">
            <div class="qq-total-progress-bar-container-selector qq-total-progress-bar-container">
                <div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-total-progress-bar-selector qq-progress-bar qq-total-progress-bar"></div>
            </div>
            <div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone>
                <span class="qq-upload-drop-area-text-selector"></span>
            </div>
            <div class="buttons">
                <div class="qq-upload-button-selector qq-upload-button">
                    <div>Select files</div>
                </div>
                <button type="button" id="trigger-upload-section1" class="btn btn-primary">
                    <i class="icon-upload icon-white"></i> Upload
                </button>
            </div>
            <span class="qq-drop-processing-selector qq-drop-processing">
                <span>Processing dropped files...</span>
                <span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
            </span>
            <ul class="qq-upload-list-selector qq-upload-list" aria-live="polite" aria-relevant="additions removals">
                <li>
                    <div class="qq-progress-bar-container-selector">
                        <div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-progress-bar-selector qq-progress-bar"></div>
                    </div>
                    <span class="qq-upload-spinner-selector qq-upload-spinner"></span>
                    <img class="qq-thumbnail-selector" qq-max-size="100" qq-server-scale>
                    <span class="qq-upload-file-selector qq-upload-file"></span>
                    <span class="qq-edit-filename-icon-selector qq-edit-filename-icon qq-editable" aria-label="Edit filename"></span>
                    <input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text">
                    <span class="qq-upload-caption-selector qq-upload-caption"></span>
                    <span class="qq-edit-caption-icon-selector qq-edit-caption-icon qq-editable" aria-label="Edit caption"></span>
                    <input class="qq-edit-caption-selector qq-edit-caption qq-editing" placeholder="Caption  here ..." tabindex="0" type="text" id="caption">
                    <span class="qq-upload-size-selector qq-upload-size"></span>
                    <button type="button" class="qq-btn qq-upload-cancel-selector qq-upload-cancel">Cancel</button>
                    <button type="button" class="qq-btn qq-upload-retry-selector qq-upload-retry">Retry</button>
                    <button type="button" class="qq-btn qq-upload-delete-selector qq-upload-delete">Delete</button>  
                    <button type="button" class="qq-btn qq-upload-delete-selector qq-upload-delete" onclick="captionUpdate();">Update Caption</button>                                      
                    <span role="status" class="qq-upload-status-text-selector qq-upload-status-text"></span>
                </li>
            </ul>

            <dialog class="qq-alert-dialog-selector">
                <div class="qq-dialog-message-selector"></div>
                <div class="qq-dialog-buttons">
                    <button type="button" class="qq-cancel-button-selector">Close</button>
                </div>
            </dialog>

            <dialog class="qq-confirm-dialog-selector">
                <div class="qq-dialog-message-selector"></div>
                <div class="qq-dialog-buttons">
                    <button type="button" class="qq-cancel-button-selector">No</button>
                    <button type="button" class="qq-ok-button-selector">Yes</button>
                </div>
            </dialog>

            <dialog class="qq-prompt-dialog-selector">
                <div class="qq-dialog-message-selector"></div>
                <input type="text">
                <div class="qq-dialog-buttons">
                    <button type="button" class="qq-cancel-button-selector">Cancel</button>
                    <button type="button" class="qq-ok-button-selector">Ok</button>
                </div>
            </dialog>
        </div>

JS

var isCaptionUpdate = false;
function captionUpdate(){
    isCaptionUpdate = true; 
};
var manualUploaderSection1 = new qq.s3.FineUploader({
    element: document.getElementById('fine-uploader-manual-trigger-section1'),
    template: 'qq-template-manual-trigger-section1',
    autoUpload: false,
    debug: true,
    request: {
        endpoint: "http://xx_my_bucket_xx.s3.amazonaws.com",
        accessKey: "AKIAIAABIA",        
    },
    signature: {
        endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php"
    },
    uploadSuccess: {
        endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php?success",
        params: {
            isBrowserPreviewCapable: qq.supportedFeatures.imagePreviews
        }
    },
    session: {
        endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php?filelist"
    },
    iframeSupport: {
        localBlankPagePath: "success.html"
    },
    cors: {
        expected: true
    },
    chunking: {
        enabled: true
    },
    resume: {
        enabled: true
    },
    deleteFile: {
        enabled: true,
        method: "POST",
        endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php",
        params: {
            caption: function() {
                if (isCaptionUpdate === true) {
                    isCaptionUpdate = false;
                    return document.getElementById('caption').value;
                }
            }
        }
    },
    validation: {
        itemLimit: 5,
        sizeLimit: 15000000
    },
    thumbnails: {
        placeholders: {
            notAvailablePath: "http://localhost/app/ci/s3.fine-uploader/placeholders/not_available-generic.png",
            waitingPath: "http://localhost/app/ci/s3.fine-uploader/placeholders/waiting-generic.png"
        }
    },
    callbacks: {
        onComplete: function(id, name, response) {
            var previewLink = qq(this.getItemByFileId(id)).getByClass('preview-link')[0];

            if (response.success) {
                previewLink.setAttribute("href", response.tempLink)
            }
        },
        onUpload: function(id, fileName) {          
            var caption = document.getElementById('caption').value;
            this.setParams({'caption':caption});
        }
    }    
});

qq(document.getElementById("trigger-upload-section1")).attach("click", function() {
    manualUploaderSection1.uploadStoredFiles();
});

Serverseitiger Code:

require '/vendor/autoload.php';
use Aws\S3\S3Client;

$clientPrivateKey = 'LB7r54Rgh9sCuTAC8V5F';
$serverPublicKey = 'AKIAU2ZEQ';
$serverPrivateKey = '8Xu6lxcDfKifHfn4pdELnM1E';

$expectedBucketName = 'xx_my_bucket_xx';
$expectedHostName = 'http://s3.amazonaws.com'; // v4-only
$expectedMaxSize = 15000000;

$method = getRequestMethod();

// This first conditional will only ever evaluate to true in a
// CORS environment
if ($method == 'OPTIONS') {
    handlePreflight();
}
// This second conditional will only ever evaluate to true if
// the delete file feature is enabled
else if ($method == "DELETE") { 
    handleCorsRequest();
    if (isset($_REQUEST['caption'])) {
        updateObjectWithCaption();
    } else {
        deleteObject();
    }
}
// This is all you really need if not using the delete file feature
// and not working in a CORS environment
else if ($method == 'POST') {
    handleCorsRequest();

    // Assumes the successEndpoint has a parameter of "success" associated with it,
    // to allow the server to differentiate between a successEndpoint request
    // and other POST requests (all requests are sent to the same endpoint in this exa,mple).
    // This condition is not needed if you don't require a callback on upload success.
    if (isset($_REQUEST["success"])) {
        verifyFileInS3(shouldIncludeThumbnail());
    }
    else {
        signRequest();
    }
}
//filelist - this is to list already uploaded files
else if ($method == 'GET') {
    if (isset($_REQUEST["filelist"])) {     
        getFileList('test/');
    }
}

function getFileList($filePrefix) { 
    global $expectedBucketName;

    $objects = getS3Client()->getIterator('ListObjects', array(
    //$objects = getS3Client()->ListObjects(array(
            'Bucket' => $expectedBucketName,
            'Prefix' => $filePrefix //must have the trailing forward slash "/"
    ));

    $object_list = array();
    foreach ($objects as $object) {
        //echo $object['Key'] . "<br>";     
        $object_metadata = getHeadObject($expectedBucketName, $object['Key']);      

        if (isset($object_metadata['Metadata']['qqfilename'])) {            
            $keyArr = explode("/", $object['Key']);         
            $posOfLastString = sizeof($keyArr) - 1;
            $uuidArry = explode(".", $keyArr[$posOfLastString]);
            $link = getTempLink($expectedBucketName, $object['Key']);

            $object_new = array();

            $object_new['name'] = $object_metadata['Metadata']['qqfilename'];
            $object_new['uuid'] = $uuidArry[0];
            $object_new['s3Key'] = $object['Key'];
            $object_new['size'] = $object['Size'];
            $object_new['s3Bucket'] = $expectedBucketName;
            $object_new['thumbnailUrl'] = $link;

            array_push($object_list, (object)$object_new);
        }
    }   
    echo json_encode($object_list); 
}

// This will retrieve the "intended" request method.  Normally, this is the
// actual method of the request.  Sometimes, though, the intended request method
// must be hidden in the parameters of the request.  For example, when attempting to
// send a DELETE request in a cross-origin environment in IE9 or older, it is not
// possible to send a DELETE request.  So, we send a POST with the intended method,
// DELETE, in a "_method" parameter.
function getRequestMethod() {
    global $HTTP_RAW_POST_DATA;

    // This should only evaluate to true if the Content-Type is undefined
    // or unrecognized, such as when XDomainRequest has been used to
    // send the request.
    if(isset($HTTP_RAW_POST_DATA)) {
        parse_str($HTTP_RAW_POST_DATA, $_POST);
    }

    if (isset($_REQUEST['_method'])) {
        return $_REQUEST['_method'];
    }

    return $_SERVER['REQUEST_METHOD'];
}

// Only needed in cross-origin setups
function handleCorsRequest() 
    // If you are relying on CORS, you will need to adjust the allowed domain here.
    header('Access-Control-Allow-Origin: http://localhost');
}

// Only needed in cross-origin setups
function handlePreflight() {
    handleCorsRequest();
    header('Access-Control-Allow-Methods: POST');
    header('Access-Control-Allow-Headers: Content-Type');
}

function getS3Client() {    
    global $serverPublicKey, $serverPrivateKey;

    return S3Client::factory(array(
        'key' => $serverPublicKey,
        'secret' => $serverPrivateKey
    ));
}

// Only needed if the delete file feature is enabled
function deleteObject() {
    getS3Client()->deleteObject(array(
        'Bucket' => $_REQUEST['bucket'],
        'Key' => $_REQUEST['key']
    ));
}

function getHeadObject($bucket, $key) { 
    $object_metadata = getS3Client()->headObject(array('Bucket' => $bucket,'Key' => $key));
    $object_metadata = $object_metadata->toArray();

    return $object_metadata;
}

function updateObjectWithCaption() {        
    $bucket = $_REQUEST['bucket'];
    $key = $_REQUEST['key'];
    $caption = $_REQUEST['caption'];
    $object_metadata = getHeadObject($bucket, $key);
    $filename = $object_metadata['Metadata']['qqfilename'];
    $fileType = getFileType($key);

    getS3Client()->copyObject(array(
            'Bucket' => $bucket,
            'Key' => $key,
            'CopySource' => urlencode($_REQUEST['bucket'] . '/' . $key),
            'MetadataDirective' => 'REPLACE',
            //'CacheControl' => 'max-age=31536000',
            //'Expires' => gmdate('D, d M Y H:i:s T', strtotime('+1 years')), // Set EXPIRES and CACHE-CONTROL headers to +1 year (RFC guidelines max.)
            'ContentType' => $fileType,
            'Metadata'=>array(
                'qqcaption' => $caption,    
                'qqfilename' => $filename,
            ),
    ));
}

function getFileType($key) {
    $file_parts = pathinfo($key);
    $filetype = "";
    switch($file_parts['extension'])
    {
        case "jpg":         
            $filetype = "image/jpeg";
            break;  
        case "jpeg":            
            $filetype = "image/jpeg";
            break;  
        case "png":         
            $filetype = "image/png";
            break;  
        case "gif":         
            $filetype = "image/gif";
            break;  
        case "tif":         
            $filetype = "image/tiff";
            break;  
        case "tiff":            
            $filetype = "image/tiff";
            break;  
        case "bmp":         
            $filetype = "image/bmp";
            break;  
    }
    return $filetype;
}

function signRequest() {
    header('Content-Type: application/json');

    $responseBody = file_get_contents('php://input');
    $contentAsObject = json_decode($responseBody, true);
    $jsonContent = json_encode($contentAsObject);

    if (!empty($contentAsObject["headers"])) {
        signRestRequest($contentAsObject["headers"]);
    }
    else {
        signPolicy($jsonContent);
    }
}

function signRestRequest($headersStr) {
    $version = isset($_REQUEST["v4"]) ? 4 : 2;
    if (isValidRestRequest($headersStr, $version)) {
        if ($version == 4) {
            $response = array('signature' => signV4RestRequest($headersStr));
        }
        else {
            $response = array('signature' => sign($headersStr));
        }

        echo json_encode($response);
    }
    else {
        echo json_encode(array("invalid" => true));
    }
}

function isValidRestRequest($headersStr, $version) {    
    if ($version == 2) {
        global $expectedBucketName;
        $pattern = "/\/$expectedBucketName\/.+$/";
    }
    else {
        global $expectedHostName;
        $pattern = "/host:$expectedHostName/";
    }

    preg_match($pattern, $headersStr, $matches);

    return count($matches) > 0;
}

function signPolicy($policyStr) {   
    $policyObj = json_decode($policyStr, true);

    if (isPolicyValid($policyObj)) {
        $encodedPolicy = base64_encode($policyStr);
        if (isset($_REQUEST["v4"])) {
            $response = array('policy' => $encodedPolicy, 'signature' => signV4Policy($encodedPolicy, $policyObj));
        }
        else {
            $response = array('policy' => $encodedPolicy, 'signature' => sign($encodedPolicy));
        }
        echo json_encode($response);
    }
    else {
        echo json_encode(array("invalid" => true));
    }
}

function isPolicyValid($policy) {   
    global $expectedMaxSize, $expectedBucketName;

    $conditions = $policy["conditions"];
    $bucket = null;
    $parsedMaxSize = null;

    for ($i = 0; $i < count($conditions); ++$i) {
        $condition = $conditions[$i];

        if (isset($condition["bucket"])) {
            $bucket = $condition["bucket"];
        }
        else if (isset($condition[0]) && $condition[0] == "content-length-range") {
            $parsedMaxSize = $condition[2];
        }
    }

    return $bucket == $expectedBucketName && $parsedMaxSize == (string)$expectedMaxSize;
}

function sign($stringToSign) {  
    global $clientPrivateKey;

    return base64_encode(hash_hmac(
            'sha1',
            $stringToSign,
            $clientPrivateKey,
            true
        ));
}

function signV4Policy($stringToSign, $policyObj) {
    global $clientPrivateKey;

    foreach ($policyObj["conditions"] as $condition) {
        if (isset($condition["x-amz-credential"])) {
            $credentialCondition = $condition["x-amz-credential"];
        }
    }

    $pattern = "/.+\/(.+)\\/(.+)\/s3\/aws4_request/";
    preg_match($pattern, $credentialCondition, $matches);

    $dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $clientPrivateKey, true);
    $dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true);
    $dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true);
    $signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true);

    return hash_hmac('sha256', $stringToSign, $signingKey);
}

function signV4RestRequest($rawStringToSign) {
    global $clientPrivateKey;

    $pattern = "/.+\\n.+\\n(\\d+)\/(.+)\/s3\/aws4_request\\n(.+)/s";
    preg_match($pattern, $rawStringToSign, $matches);

    $hashedCanonicalRequest = hash('sha256', $matches[3]);
    $stringToSign = preg_replace("/^(.+)\/s3\/aws4_request\\n.+$/s", '$1/s3/aws4_request'."\n".$hashedCanonicalRequest, $rawStringToSign);

    $dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $clientPrivateKey, true);
    $dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true);
    $dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true);
    $signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true);

    return hash_hmac('sha256', $stringToSign, $signingKey);
}

// This is not needed if you don't require a callback on upload success.
function verifyFileInS3($includeThumbnail) {
    global $expectedMaxSize;

    $bucket = $_REQUEST["bucket"];
    $key = $_REQUEST["key"];

    // If utilizing CORS, we return a 200 response with the error message in the body
    // to ensure Fine Uploader can parse the error message in IE9 and IE8,
    // since XDomainRequest is used on those browsers for CORS requests.  XDomainRequest
    // does not allow access to the response body for non-success responses.
    if (isset($expectedMaxSize) && getObjectSize($bucket, $key) > $expectedMaxSize) {
        // You can safely uncomment this next line if you are not depending on CORS
        header("HTTP/1.0 500 Internal Server Error");
        deleteObject();
        echo json_encode(array("error" => "File is too big!", "preventRetry" => true));
    }
    else {
        $link = getTempLink($bucket, $key);
        $response = array("tempLink" => $link);

        if ($includeThumbnail) {
            $response["thumbnailUrl"] = $link;
        }

        echo json_encode($response);
    }
}

// Provide a time-bombed public link to the file.
function getTempLink($bucket, $key) {   
    $client = getS3Client();
    $url = "{$bucket}/{$key}";
    $request = $client->get($url);

    return $client->createPresignedUrl($request, '+15 minutes');
}

function getObjectSize($bucket, $key) { 
    $objInfo = getS3Client()->headObject(array(
            'Bucket' => $bucket,
            'Key' => $key
        ));
    return $objInfo['ContentLength'];
}

// Return true if it's likely that the associate file is natively
// viewable in a browser.  For simplicity, just uses the file extension
// to make this determination, along with an array of extensions that one
// would expect all supported browsers are able to render natively.
function isFileViewableImage($filename) {   
    $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
    $viewableExtensions = array("jpeg", "jpg", "gif", "png", "tif", "tiff");

    return in_array($ext, $viewableExtensions);
}

// Returns true if we should attempt to include a link
// to a thumbnail in the uploadSuccess response.  In it's simplest form
// (which is our goal here - keep it simple) we only include a link to
// a viewable image and only if the browser is not capable of generating a client-side preview.
function shouldIncludeThumbnail() { 
    $filename = $_REQUEST["name"];
    $isPreviewCapable = $_REQUEST["isBrowserPreviewCapable"] == "true";
    $isFileViewableImage = isFileViewableImage($filename);

    return !$isPreviewCapable && $isFileViewableImage;
}

Antworten auf die Frage(2)

Ihre Antwort auf die Frage