2010-02-04 3 views
44

Я пытаюсь подделать загрузку файла, фактически не используя файл ввода от пользователя. Содержимое файла будет динамически генерироваться из строки.Javascript: Загрузка файла ... без файла

Возможно ли это? Кто-нибудь когда-либо делал это раньше? Существуют ли примеры/теория?

Чтобы уточнить, я знаю, как загрузить файл с использованием методов AJAX с помощью скрытого iframe и друзей - проблема заключается в загрузке файла, который не находится в форме.

Я использую ExtJS, но jQuery возможен, так как ExtJS может подключаться к нему (ext-jquery-base).

+1

Я сомневаюсь, что это возможно, но интересный вопрос, +1 –

+0

Это кажется неправильным решение вашей проблемы (если у вас есть контроль над серверной стороной). Если содержимое файла будет сгенерировано из строки, почему бы не просто выполнить POST эту строку и создать файл на сервере (используя PHP или что-то еще)? Если вы загружаете файл третьему лицу, то игнорируйте этот комментарий. –

+0

@JonathanJulian, несмотря ни на что, этот usecase пахнет реальной взломанной стоимостью -), потрясающий трюк! – nemesisfixx

ответ

30

Почему бы не просто использовать XMLHttpRequest() с 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)); 
} 

Скрипт обработчика на сервере просто записывает данные файла в файл.

EDIT
загрузить файл по-прежнему HTTP POST с различным типом контента. Вы можете использовать этот тип контента и отделить содержание с границами:

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); 
} 

Если вы хотите отправить дополнительные данные, вы просто разделить каждую секцию с границей и описать содержание-расположение и типа содержимого заголовков для каждой секции , Каждый заголовок разделяется новой строкой, и тело отделено от заголовков дополнительной новой строкой. Естественно, загрузка двоичных данных таким образом будет несколько сложнее :-)

Дальнейшее редактирование: забыли упомянуть, убедитесь, что какая-либо граничная строка отсутствует в текстовом «файле», который вы отправляете, иначе это будет рассматриваться как граница.

+0

Поскольку сервер не распознает его как загруженный «файл». – LiraNuna

+0

Я думаю, он хочет знать, как генерировать «данные». –

+0

@LiraNuna: Почему это имеет значение, если вы генерируете контент из строки? Разве он не может распознать его как строку и написать ее? –

6

Загрузка файла Это всего лишь POST запрос с соответствующим кодированием содержимого и специальным заголовком multipart/formdata. Вы должны использовать это <input type=file />, потому что ваша безопасность браузера запрещает вам напрямую обращаться к пользовательскому диску.

Поскольку вам не нужно читать пользовательский диск, ДА, вы можете подделать его с помощью Javascript. Это будет всего лишь XMLHttpRequest. Чтобы создать «аутентичный» запрос на загрузку, вы можете установить Fiddler и проверить свой исходящий запрос.

Вам нужно правильно кодировать этот файл, так что эта ссылка может быть очень полезной: RFC 2388: Returning Values from Forms: multipart/form-data

+0

Что следует делать в этом запросе? как определяется этот протокол? как подделать? – LiraNuna

+0

, который не является протоколом, это обычный HTTP-запрос; Я обновил свой ответ –

+0

Я не использовал Fiddler (здесь пользователь Linux), но Firebug показывает, как он должен выглядеть. Это приближает меня на один шаг. Я уверен, что это полезно, но пока не выбирая ответ. – LiraNuna

3

Я просто поймал эту POST_DATA строку с аддоном Firefox TamperData. Я отправил форму с одним полем type="file" с именем «myfile» и кнопкой отправки «btn-submit» со значением «Загрузить». Содержимое загруженного файла являются

Line One 
Line Two 
Line Three 

Так вот 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 

Я не уверен, что означает, что число (192642264827446), но это не должно быть слишком трудно найти вне.

+0

Я переформатировал POST_DATA, чтобы упростить чтение, 192642264827446 выглядит как граничный маркер –

+0

Спасибо, gnibbler. Да, я думал, что это может быть что-то вроде пограничного маркера, возможно, просто случайное число. –

+1

Да, это пограничный маркер. Если вы проверите заголовок 'multipart/form-data', граница будет следовать за ним. Случайное число в конце состоит в том, чтобы избежать любых конфликтов с отправляемыми данными. –

13

Простое разделение конечного результата, который работает - и имеет чистый способ добавления/удаления параметров без какого-либо жесткого кодирования.

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 
    } 
}); 

Это был протестирован для работы на всех современных браузерах, в том числе, но не ограничиваясь:

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

+1 Хорошее решение.Но я думаю, что что-то не так с вашим алгоритмом. Почему вы используете 'for in' для объекта params? Он швы, как будто он подготовлен для нескольких файлов, но второй файл, как будет называться в объекте? Где используются 'action',' overwrite' и 'destination'? и как они не нарушают код внутри 'for in'? –

+0

@Protron: Причина, по которой я использую 'for (in)', - это получить ключи от объекта описания. Код обнаружит, установлено ли 'filename' на вложенном объекте (который описывает файл для загрузки). Другие параметры ('overwrite',' action', 'destination') - это дополнительные параметры, передаваемые, как если бы вы использовали форму. – LiraNuna

+2

@ LiraNuna, я вижу, что вы, ребята, все волшебники о '-----------------------------', единственное требование спецификация MIME (см. RFC 1341, раздел 7.2.1) заключается в том, что граница начинается с '- ', следующего за действительным токеном (см. RFC 1341, раздел 4). Надеюсь, это поможет другим узнать их свободу тоже :-) – nemesisfixx

2

https://stackoverflow.com/a/2198524/2914587 работал для меня, после того, как я добавил дополнительный '--', прежде чем в полезной нагрузке конечного boundary:

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

Если вам не нужна поддержка старых браузеров вы можете использовать объект FormData, который является частью 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); 

Api файла поддерживается всеми текущими браузерами (IE10 +)

+0

Это сработало лучше для меня. благодаря! – trudesign

+2

Я избегаю писать свои собственные XMLHttpRequests. Это, безусловно, мой предпочтительный ответ! – Chris

+0

Это сработало для меня. Спасибо, что поделился. –

0

Легкий способ имитировать «фальшивый» загрузить файл с 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 
}); 
Смежные вопросы