5

Questo problema mi ha infastidito per molte ore e non riesco a trovare una soluzione.Caricamento di più file direttamente su Amazon S3 utilizzando Rails 3.2 e AJAX (soluzioni di caricamento non flash)

Ho un'app per rails 3.2 che consente agli utenti di caricare file su un account Amazon S3 utilizzando carrierwave_direct, fog e carrierwave (dipendenza per carrierwave_direct). L'utilizzo di carrierwave_direct consente all'utente di saltare il caricamento del file sul server eseguendo il POST direttamente su Amazon S3 (salva l'elaborazione del server e timeout come Heroku per file di grandi dimensioni).

Funziona bene se tutto ciò che si fa è selezionare 1 file, caricarlo su Amazon e volere un reindirizzamento a un URL che si fornisce Amazon. Lo fa inserendo il modulo in Amazon S3 e Amazon risponde a un URL fornito (si specifica questo URL nel modulo) con alcuni parametri nell'URL, che vengono quindi memorizzati come puntatore del file su Amazon nel modello.

Quindi il ciclo di vita è: selezionare 1 file, POST su Amazon, Amazon risponde con un URL che invia a un'altra pagina e quindi è possibile salvare un record con un puntatore al file Amazon.

Quello che ho cercato di capire è come consentire a più file di essere selezionati e caricati e di aggiornare i progressi del caricamento? Sto provando a farlo con puro javascript (usando l'API dei file fornita dai browser moderni), quindi non voglio strumenti di terze parti. Inoltre, nel perseguire l'apprendimento di questo approfondimento, sto evitando qualsiasi plugin e sto provando a scrivere il codice da solo.

La funzionalità che sto cercando di ottenere è:

  1. utente vede modulo con il campo di file (o drag/drop) seleziona
  2. utente più file (fare clic campo file o drag/drop)
  3. utilizzando Javascript (nessun server ancora), costruire una coda di file selezionati da caricare (solo nome del file e le dimensioni, utilizzando API del browser di file)
  4. utente quindi fa clic su un "begin Carica" ​​pulsante
  5. iterare su ogni file nella coda un d POSTARE il file su Amazon S3; Amazon risponderà a ogni singolo POST con un URL e tale URL dovrà essere gestito tramite Javascript, non come richiesta standard; l'URL fornito da Amazon creerà un record che memorizza il puntatore sul file Amazon; una volta creato il record, il codice passa al file successivo nella coda fino al termine.

A questo punto, potrei anche fare a meno di una barra di avanzamento individuale; Sarei felice solo di ricevere più file inviati su Amazon S3 senza aggiornare la pagina.

Non sono parziale a nessuna delle gemme. In realtà ho paura che dovrò scrivere ciò che voglio fare da zero se voglio davvero farlo in un modo specifico. L'obiettivo è il caricamento di più file su un account Amazon S3 tramite AJAX. Sarei estasiato anche con concetti generali su come affrontare il problema. Ho passato molte ore a cercarlo su google e non ho trovato nessuna soluzione che faccia ciò che voglio. Qualsiasi aiuto sarebbe molto apprezzato.

EDIT 2014-03-02

Raj chiesto come ho implementato il mio caricamento multiplo. È passato così tanto tempo che non ricordo tutti i "perché" di quello che ho fatto (probabilmente codice errato comunque come era la mia prima volta), ma ecco quello che stavo succedendo.

Il modello che stavo caricando era un Testimonial, che ha un'immagine associata memorizzata in Amazon S3.Permetteva a un utente di selezionare più immagini (penso che fossero in realtà file PDF convertiti in immagini) e li trascina/rilasciali sullo schermo. Durante il caricamento, ho visualizzato una modale che dava all'utente il feedback su quanto tempo ci sarebbe voluto.

Non pretendo di ricordare quello che stavo facendo su molto di questo, ma se aiuta a sentirsi libero di usarlo.

# Gemfile 
# For client-side multiple uploads 
gem "jquery-fileupload-rails" 

# For file uploads and Amazon S3 storage 
gem "rmagick" 
gem "carrierwave" 
gem "fog" 

Ecco la vista:

# app/views/testimonials/new.html.erb 
<div id="main" class="padded"> 
    <div class="center"> 
    <div id="dropzone"> 
     Click or Drop Files here to Upload 
    </div> 

    <%= form_for @testimonial do |f| %> 
     <div class="field"> 
     <%= file_field_tag :image, multiple: true, name: "testimonial[image]", id: "testimonial_image" %> 
     </div> 
    <% end %> 
    </div> 
</div> 

<div id="mask"></div> 
<div id="modal"> 
    <h1> 
    Uploading <span id="global-upload-count">0</span> Files... 
    </h1> 
    <div id="global-progress"> 
    <div id="global-progress-bar" style="width: 0%"> 
     <div id="global-progress-percentage">0%</div> 
    </div> 
    </div> 
    <div id="global-processing"> 
    <span class="spinner"></span> Processing...<span id="global-processing-count">0</span> sec 
    </div> 
</div> 

<script id="template-upload" type="text/x-tmpl"> 
    <div class="upload"> 
    {%=o.name%} ({%=o.readable_size%}) 
    <div class="float-right percentage"></div> 
    <div class="progress"><div class="bar" style="width: 0%"></div></div> 
    </div> 
</script> 

E il JS:

number_to_human_size = (bytes) -> 
    sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'] 
    i = parseInt(Math.floor(Math.log(bytes)/Math.log(1024))) 
    return Math.round(bytes/Math.pow(1024, i), 2) + ' ' + sizes[i] 

dropzone_hover = (e) -> 
    e.preventDefault() 
    $(this).addClass("dropzone-hover") 

dropzone_leave = (e) -> 
    e.preventDefault() 
    $(this).removeClass("dropzone-hover") 

jQuery -> 
    global_count = 0 
    seconds_to_process = 0 
    processing_factor = 5 # seconds to convert/process each uploaded file 

    $("#testimonial_image").hide() 

    dropzone = $("#dropzone") 

    dropzone.bind "click", (e) -> 
    $("#testimonial_image").click() 

    dropzone.bind("dragover", dropzone_hover) 
    dropzone.bind("dragleave", dropzone_leave) 
    dropzone.bind("drop", dropzone_leave) 

    $("#new_testimonial").data("global-count", "0") 

    $("#new_testimonial").fileupload 
    dropZone: $("#dropzone") 
    maxFileSize: 5000000 # 5 MB 
    dataType: "script" 

    add: (e, data) -> 
     file = data.files[0] 
     file.readable_size = number_to_human_size(file.size) 
     data.context = $(tmpl("template-upload", file).trim()) 
     $("#new_testimonial").append(data.context) 
     data.submit() 
     global_count += 1 

    progress: (e, data) -> 
     if data.context 
     progress = parseInt(data.loaded/data.total * 100, 10) 
     data.context.find(".bar").css("width", progress + "%") 
     data.context.find(".percentage").text(progress + "%") 

    submit: (e, data) -> 
     $("#mask").show() 
     $("#modal").center().show() 

    progressall: (e, data) -> 
     $("#global-upload-count").text(global_count) 
     global_progress = parseInt(data.loaded/data.total * 100, 10) 
     $("#global-progress-bar").css("width", global_progress + "%") 
     $("#global-progress-percentage").text(global_progress + "%") 

     if global_progress >= 100 
     seconds_to_process = global_count * processing_factor 
     $("#global-processing-count").text(seconds_to_process) 

     $("#global-processing").show() 

     timer = setInterval(-> 
      seconds_to_process = seconds_to_process - 1 
      $("#global-processing-count").text(seconds_to_process) 

      if seconds_to_process == 0 
      clearInterval(timer) 
      global_count = 0 
      seconds_to_process = 0 
      $("#modal, #mask").hide(0) 
     , 1000) 

Il modello Testimonial:

class Testimonial < ActiveRecord::Base 
    mount_uploader :image, ImageUploader 

    def display_name 
    if name.blank? 
     return "Testimonial #{self.id}" 
    else 
     return name 
    end 
    end 
end 
+2

Utilizzare questo: http://blueimp.github.com/jQuery-File-Upload/ – apneadiving

+0

@apneadiving che ha funzionato esattamente come volevo. La pagina del plugin ha alcuni esempi molto utili che mi hanno indirizzato nella giusta direzione e ho implementato esattamente quello che volevo. Se rispondi alla domanda invece di un commento, accetterò volentieri la tua risposta. Grazie mille, mi hai aiutato a salvarmi un sacco di sforzi. –

+0

Piacere di leggere :) – apneadiving

risposta

6

come consigliato in commento, utilizzare jQuery Carica: http://blueimp.github.com/jQuery-File-Upload/

+7

Sebbene ciò possa teoricamente rispondere alla domanda, [sarebbe preferibile] (http://meta.stackexchange.com/q/8259) includere qui le parti essenziali della risposta e fornire il link per riferimento. –

+1

@IlmariKaronen Ho postato su richiesta del richiedente. Ma sentitevi liberi di downvotare se vi piace – apneadiving

+0

Qualcuno ha contrassegnato la vostra risposta come "non una risposta". Non sono del tutto d'accordo, ma * è * piuttosto borderline. Detto questo, la domanda non è esattamente la più chiara di sempre. –

0

Ho iniziato a scrivere una libreria di base per questa funzionalità. Ho una versione funzionante su github e sto scrivendo una serie di post sul blog per sapere come ottenere ciò.

Il codice "Working" è disponibile al numero: https://github.com/joeandrews/s3multipartupload.

Problemi correlati