2010-02-04 19 views
44

Sto tentando di simulare un caricamento di file senza utilizzare effettivamente un file di input dall'utente. Il contenuto del file verrà generato dinamicamente da una stringa.Javascript: caricamento di un file ... senza file

È possibile? Qualcuno l'ha mai fatto prima? Ci sono esempi/teoria disponibili?

Per chiarire, so come caricare un file utilizzando tecniche AJAX utilizzando un iframe nascosto e gli amici - il problema è il caricamento di un file che non è nel modulo.

Sto usando ExtJS, ma jQuery è fattibile anche da quando ExtJS può collegarlo (ext-jquery-base).

+1

Dubito che sia possibile, ma interessante domanda, +1 –

+0

Questa sembra la soluzione sbagliata al vostro problema (se hai il controllo del lato server). Se il contenuto del file verrà generato da una stringa, perché non solo POST quella stringa e creare il file sul server (utilizzando PHP o qualsiasi altra cosa)? Se si sta caricando un file su una destinazione di terze parti, ignorare questo commento. –

+0

@JonathanJulian, non importa cosa, questo usecase odora di valore di hack reale -), fantastico trucco! – nemesisfixx

risposta

30

Perché non utilizzare lo XMLHttpRequest() con il POST?

function beginQuoteFileUnquoteUpload(data) 
{ 
    var xhr = new XMLHttpRequest(); 
    xhr.open("POST", "http://www.mysite.com/myuploadhandler.php", true); 
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
    xhr.onreadystatechange = function() 
    { 
     if (xhr.readyState == 4 && xhr.status == 200) 
      alert("File uploaded!"); 
    } 
    xhr.send("filedata="+encodeURIComponent(data)); 
} 

Lo script del gestore sul server scrive solo i dati del file in un file.

EDIT
upload di file ha ancora un HTTP POST con un diverso tipo di contenuto. È possibile utilizzare questo tipo di contenuto e separare i contenuti con i confini:

function beginQuoteFileUnquoteUpload(data) 
{ 
    // Define a boundary, I stole this from IE but you can use any string AFAIK 
    var boundary = "---------------------------7da24f2e50046"; 
    var xhr = new XMLHttpRequest(); 
    var body = '--' + boundary + '\r\n' 
      // Parameter name is "file" and local filename is "temp.txt" 
      + 'Content-Disposition: form-data; name="file";' 
      + 'filename="temp.txt"\r\n' 
      // Add the file's mime-type 
      + 'Content-type: plain/text\r\n\r\n' 
      + data + '\r\n' 
      + boundary + '--'; 

    xhr.open("POST", "http://www.mysite.com/myuploadhandler.php", true); 
    xhr.setRequestHeader(
     "Content-type", "multipart/form-data; boundary="+boundary 

    ); 
    xhr.onreadystatechange = function() 
    { 
     if (xhr.readyState == 4 && xhr.status == 200) 
      alert("File uploaded!"); 
    } 
    xhr.send(body); 
} 

Se si desidera inviare i dati aggiuntivi, basta separare ogni sezione con un confine e descrivere le intestazioni Content-Disposition e tipo di contenuto per ogni sezione . Ogni intestazione è separata da una nuova riga e il corpo viene separato dalle intestazioni da una nuova riga aggiuntiva. Ovviamente, caricare i dati binari in questo modo sarebbe leggermente più difficile :-)

Ulteriori modifiche: dimenticato di menzionare, assicurati che qualsiasi stringa di limite non sia nel "file" di testo che stai inviando, altrimenti lo farà essere trattati come un limite.

+0

Perché il server non lo riconoscerà come un "file" caricato. – LiraNuna

+0

Penso che voglia sapere come generare 'data'. –

+0

@LiraNuna: perché importa se stai generando il contenuto da una stringa? Non è possibile riconoscerlo come una stringa e scriverlo? –

6

Un caricamento di file è solo una richiesta POST con il contenuto del file correttamente codificato e con un'intestazione speciale multipart/formdata. Devi usare quello <input type=file /> perché la sicurezza del tuo browser ti impedisce di accedere direttamente al disco dell'utente.

Poiché non è necessario leggere il disco utente, SI, è possibile simularlo utilizzando Javascript. Sarà solo un XMLHttpRequest. Per creare una richiesta di caricamento "autentica", è possibile installare Fiddler e ispezionare la richiesta in uscita.

Avrai bisogno di codificare correttamente il file, in modo da questo link può essere molto utile: RFC 2388: Returning Values from Forms: multipart/form-data

+0

Cosa dovrebbe andare in quella richiesta allora? come viene definito quel protocollo? come fingere? – LiraNuna

+0

che non è un protocollo, è solo una normale richiesta HTTP; Ho aggiornato la mia risposta –

+0

Non ho usato Fiddler (utente Linux qui), ma Firebug mostra come dovrebbe apparire. Questo mi avvicina di un passo. Sto upvoting in quanto è utile, ma non ancora selezionando la risposta. – LiraNuna

3

Ho appena preso questa stringa post_data con l'addon Firefox TamperData. Ho inviato un modulo con un campo type="file" denominato "myfile" e un pulsante di invio denominato "btn-submit" con valore "Carica". Il contenuto del file caricato sono

Line One 
Line Two 
Line Three 

ecco la stringa post_data:

-----------------------------192642264827446\r\n 
Content-Disposition: form-data; \n 
name="myfile"; filename="local-file-name.txt"\r\n 
Content-Type: text/plain\r\n 
\r\n 
Line \n 
One\r\n 
Line Two\r\n 
Line Three\r\n 
\r\n 
-----------------------------192642264827446\n 
\r\n 
Content-Disposition: form-data; name="btn-submit"\r\n 
\r\n 
Upload\n 
\r\n 
-----------------------------192642264827446--\r\n 

Non sono sicuro di cosa si intende il numero (192.642.264.827.446), ma che non dovrebbe essere troppo difficile da trovare su.

+0

Ho riformattato il POST_DATA per renderlo più facile da leggere, il 192642264827446 sembra un marker di confine –

+0

Grazie, gnibbler. Sì, ho pensato che potesse essere qualcosa come un marcatore di confine, probabilmente solo un numero casuale. –

+1

Sì, è un indicatore di confine. Se controlli l'intestazione 'multipart/form-data', il confine lo seguirà. Il numero casuale alla fine serve ad evitare qualsiasi conflitto con i dati inviati. –

13

Basta condividere il risultato finale, che funziona, e ha un modo pulito di aggiungere/rimuovere parametri senza alcun hardcoding.

var boundary = '-----------------------------' + 
      Math.floor(Math.random() * Math.pow(10, 8)); 

    /* Parameters go here */ 
var params = { 
    file: { 
     type: 'text/plain', 
     filename: Path.utils.basename(currentTab.id), 
     content: GET_CONTENT() /* File content goes here */ 
    }, 
    action: 'upload', 
    overwrite: 'true', 
    destination: '/' 
}; 

var content = []; 
for(var i in params) { 
    content.push('--' + boundary); 

    var mimeHeader = 'Content-Disposition: form-data; name="'+i+'"; '; 
    if(params[i].filename) 
     mimeHeader += 'filename="'+ params[i].filename +'";'; 
    content.push(mimeHeader); 

    if(params[i].type) 
     content.push('Content-Type: ' + params[i].type); 

    content.push(''); 
    content.push(params[i].content || params[i]); 
}; 

    /* Use your favorite toolkit here */ 
    /* it should still work if you can control headers and POST raw data */ 
Ext.Ajax.request({ 
    method: 'POST', 
    url: 'www.example.com/upload.php', 
    jsonData: content.join('\r\n'), 
    headers: { 
     'Content-Type': 'multipart/form-data; boundary=' + boundary, 
     'Content-Length': content.length 
    } 
}); 

Questo è stato testato per funzionare su tutti i browser moderni, compreso ma non limitato a:

  • IE6 +
  • FF 1.5 +
  • Opera 9+
  • Chrome 1.0+
  • Safari 3.0+
+0

+1 Soluzione piacevole.Ma penso che ci sia qualcosa di sbagliato nel tuo algoritmo. Perché usi un 'for in' per l'oggetto params? Sembra che sia stato preparato per più di un file, ma il secondo file come sarà chiamato nell'oggetto? Dove sono usati 'azione',' sovrascrivi' e 'destinazione'? e come non infrangono il codice all'interno di "for in"? –

+0

@Protron: Il motivo per cui utilizzo 'for (in)' è quello di ottenere le chiavi dall'oggetto description. Il codice rileverà se 'filename' è impostato su un oggetto nidificato (che descrive un file da caricare). Gli altri parametri ('overwrite',' action', 'destination') sono solo parametri extra passati come se si usasse un modulo. – LiraNuna

+2

@LiraNuna, vedo tutti voi ragazzi diventare tutti magici riguardo '-----------------------------', l'unico requisito di la specifica MIME (vedi RFC 1341, secondo 7.2.1) è che il confine inizia con '--' seguito da un token valido (vedi RFC 1341 sez.4). Spero che questo aiuti gli altri a conoscere anche la loro libertà :-) – nemesisfixx

2

https://stackoverflow.com/a/2198524/2914587 lavorato per me, dopo ho aggiunto un extra '--' prima della finale boundary nel payload:

var body = '--' + boundary + '\r\n' 
     // Parameter name is "file" and local filename is "temp.txt" 
     + 'Content-Disposition: form-data; name="file";' 
     + 'filename="temp.txt"\r\n' 
     // Add the file's mime-type 
     + 'Content-type: plain/text\r\n\r\n' 
     + data + '\r\n' 
     + '--' + boundary + '--'; 
15

Se non avete bisogno di supporto per i browser meno recenti è possibile utilizzare l'oggetto formdata, che fa parte del File API:

var formData = new FormData(); 
var blob = new Blob(['Lorem ipsum'], { type: 'plain/text' }); 
formData.append('file', blob,'readme.txt'); 

var request = new XMLHttpRequest(); 
request.open('POST', 'http://example.org/upload'); 
request.send(formData); 

File Api è supportato da tutti i browser attuali (IE10 +)

+0

Questo ha funzionato meglio per me. Grazie! – trudesign

+2

Evito di scrivere il mio XMLHttpRequests. Questa è sicuramente la mia risposta preferita! – Chris

+0

Questo ha funzionato per me. Grazie per la condivisione. –

0

Facile modo per imitare "falso" upload di file con jQuery:

var fd = new FormData(); 
var file = new Blob(['file contents'], {type: 'plain/text'}); 

fd.append('formFieldName', file, 'fileName.txt'); 

$.ajax({ 
    url: 'http://example.com/yourAddress', 
    method: 'post', 
    data: fd, 
    processData: false,  //this... 
    contentType: false   //and this is for formData type 
}); 
Problemi correlati