перетащите файлы в стандартный HTML-файл

163

В наши дни мы можем перетаскивать файлы в специальный контейнер и загружать их с помощью XHR 2. Многие за один раз. С живыми прогресс-барами и т. Д. Очень классные вещи. Пример тут.

Но иногда мы не хотим такой крутости. То , что я хотел бы , чтобы перетащить и падение файлов - многие в то время - в стандартный ввод HTML файла : <input type=file multiple>.

Это возможно? Есть ли какой-нибудь способ «заполнить» файл ввода правильными именами файлов (?) Из файла drop? (Полные пути к файлам недоступны по соображениям безопасности файловой системы.)

Зачем? Потому что я хотел бы отправить нормальную форму. Для всех браузеров и всех устройств. Перетаскивание - это просто прогрессивное улучшение для улучшения и упрощения UX. Там будет стандартная форма со стандартным вводом файла (+ multipleатрибут). Я хотел бы добавить улучшение HTML5.

править
Я знаю , в некоторых браузерах вы можете иногда (почти всегда) перетаскивать файлы в сам файл ввод. Я знаю, что Chrome обычно делает это, но иногда он терпит неудачу, а затем загружает файл на текущей странице (большая ошибка, если вы заполняете форму). Я хочу обмануть и браузер.

Rudie
источник
1
Приготовьтесь к некоторой боли, если вы хотите включить mac / safari в вашу совместимость.
Shark8
1
@ Shark8 на самом деле Safari / Mac - один из немногих браузеров, уже поддерживающих это.
Рикардо Томази
На самом деле, ни один из браузеров не поддерживает это. Поле ввода файла доступно только для чтения (для безопасности), и в этом проблема. Тупая безопасность!
Руди
2
Под этим я подразумевал «перетаскивание файлов - по нескольку одновременно - в стандартный ввод HTML-файла».
Рикардо Томази
3
Перетащите несколько файлов, чтобы input type="file" multipleработать хорошо в Safari
Lloyd

Ответы:

71

Следующее работает в Chrome и FF, но мне еще предстоит найти решение, которое также охватывает IE10 +:

// dragover and dragenter events need to have 'preventDefault' called
// in order for the 'drop' event to register. 
// See: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations#droptargets
dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
  evt.preventDefault();
};

dropContainer.ondrop = function(evt) {
  // pretty simple -- but not for IE :(
  fileInput.files = evt.dataTransfer.files;

  // If you want to use some of the dropped files
  const dT = new DataTransfer();
  dT.items.add(evt.dataTransfer.files[0]);
  dT.items.add(evt.dataTransfer.files[3]);
  fileInput.files = dT.files;

  evt.preventDefault();
};
<!DOCTYPE html>
<html>
<body>
<div id="dropContainer" style="border:1px solid black;height:100px;">
   Drop Here
</div>
  Should update here:
  <input type="file" id="fileInput" />
</body>
</html>

Возможно, вы захотите использовать addEventListenerили jQuery (и т. Д.) Для регистрации ваших обработчиков evt - это просто для краткости.

JLB
источник
3
Waaaaaaaat! Это работает!? Это именно то , что я искал. Не работал 2 года назад. Потрясающие! Конечно, это не работает в IE =) Важный вопрос: есть ли надежное обнаружение функции ?, так что вы можете скрыть зону сброса в IE, потому что она не будет работать.
Руди
Да, немного поздно :) Сейчас я просто использую простые проверки пользовательских агентов в JS. Конечно, вы должны проверить MSIE , Trident/(IE11) и Edge/(IE12) ...
JLB
FF 48.0.2 (Mac) выдает «TypeError: установка свойства, которое имеет только геттер» в строке fileInput.files = evt.dataTransfer.files;. Safari и Chrome работают нормально.
Risadinha
2
Этот пример не работает на Firefox 45 на Linux, но он работает для меня на Chrome. Я не получаю никаких ошибок консоли, это просто не показывает, что какой-либо файл был удален.
Брайан Оукли
1
на самом деле я сделал пост, чтобы попытаться найти решение, но выяснил для себя. Довольно простое изменение, просто fileInputs [index] = ..., чтобы передать данные файла на определенный вход, а затем вызвать функцию showNext, чтобы добавить новый вход stackoverflow.com/a/43397640/6392779
ник
51

Я сделал решение для этого.

Функция перетаскивания для этого метода работает только с Chrome, Firefox и Safari. (Не знаю, работает ли он с IE10), но для других браузеров кнопка «Или нажмите здесь» работает нормально.

Поле ввода просто следует за мышью при перетаскивании файла на область, и я также добавил кнопку ..

Непрозрачность непрозрачности: 0; ввод файла виден только для того, чтобы вы могли видеть, что происходит.

BjarkeCK
источник
Вот почему я добавил кнопку также ^^ Но ты прав. Я бы не стал пользоваться ею ... Или я?
BjarkeCK
Хотел бы я знать, как это должно работать ... кажется, что все функции перетаскивания должны иметь дело с добавлением эффекта наведения ... но я действительно не могу сказать. Выглядит хорошо в скрипке, но я не думаю, что смогу использовать его, так как мне нужно поддерживать Internet Explorer
nmz787
1
@PiotrKowalski Я думаю, что это может вызвать рекурсивный вызов, пока стек вызовов не переполнится
Джон
2
Я закончил тем, что использовал только стиль. Создание 100% ширины и высоты ввода работало лучше, чем его перемещение.
Эдди
2
Есть ли способ избавиться от «не выбран файл», который продолжает зависать вместе с указателем мыши? @BjarkeCK
Абхишек Сингх,
27

Это «HTML5» способ сделать это. Нормальный ввод формы (который только для чтения, как указал Рикардо Томази). Затем, если файл перетаскивается, он прикрепляется к форме. Для этого потребуется изменить страницу действий, чтобы принять загруженный таким образом файл.

function readfiles(files) {
  for (var i = 0; i < files.length; i++) {
    document.getElementById('fileDragName').value = files[i].name
    document.getElementById('fileDragSize').value = files[i].size
    document.getElementById('fileDragType').value = files[i].type
    reader = new FileReader();
    reader.onload = function(event) {
      document.getElementById('fileDragData').value = event.target.result;}
    reader.readAsDataURL(files[i]);
  }
}
var holder = document.getElementById('holder');
holder.ondragover = function () { this.className = 'hover'; return false; };
holder.ondragend = function () { this.className = ''; return false; };
holder.ondrop = function (e) {
  this.className = '';
  e.preventDefault();
  readfiles(e.dataTransfer.files);
}
#holder.hover { border: 10px dashed #0c0 !important; }
<form method="post" action="http://example.com/">
  <input type="file"><input id="fileDragName"><input id="fileDragSize"><input id="fileDragType"><input id="fileDragData">
  <div id="holder" style="width:200px; height:200px; border: 10px dashed #ccc"></div>
</form>

Еще лучше, если вы можете сделать все окно зоной перетаскивания, см. Как я могу обнаружить событие перетаскивания HTML5, входящее и выходящее из окна, как это делает Gmail?

Уильям Энтрикен
источник
1
Хорошее решение, но оно не работает на IE <10, потому что IE 9 и меньше не поддерживает API файлов HTML5 :(
Develoger
1
Эта строка: document.getElementById ('fileDragData'). Value = files [i] .slice (); не нужен, потому что заменяется в функции
reader.onload
Вот еще одно симпатичное приложение для перетаскивания, которое НЕ включает загрузку файлов. Ссылки на случай, если кто-то захочет больше учиться. codepen.io/anon/pen/MOPvZK?editors=1010
Уильям Энтрикен
1
Решение IE 10 состоит в том, чтобы ухудшать и показывать толькоinput type=file
Уильям Энтрикен
Я что-то упустил, или вы просто постоянно перезаписываете .valueсвойство самым новым файлом каждый раз, когда выполняете цикл fore fore?
Кевин Берк
13

//----------App.js---------------------//
$(document).ready(function() {
    var holder = document.getElementById('holder');
    holder.ondragover = function () { this.className = 'hover'; return false; };
    holder.ondrop = function (e) {
      this.className = 'hidden';
      e.preventDefault();
      var file = e.dataTransfer.files[0];
      var reader = new FileReader();
      reader.onload = function (event) {
          document.getElementById('image_droped').className='visible'
          $('#image_droped').attr('src', event.target.result);
      }
      reader.readAsDataURL(file);
    };
});
.holder_default {
    width:500px; 
    height:150px; 
    border: 3px dashed #ccc;
}

#holder.hover { 
    width:400px; 
    height:150px; 
    border: 3px dashed #0c0 !important; 
}

.hidden {
    visibility: hidden;
}

.visible {
    visibility: visible;
}
<!DOCTYPE html>

<html>
    <head>
        <title> HTML 5 </title>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
    </head>
    <body>
      <form method="post" action="http://example.com/">
        <div id="holder" style="" id="holder" class="holder_default">
          <img src="" id="image_droped" width="200" style="border: 3px dashed #7A97FC;" class=" hidden"/>
        </div>
      </form>
    </body>
</html>

Дипак
источник
2
Что это показывает пользователю? Можете ли вы сделать скрипку или онлайн пример?
Руди
@Rudie, пожалуйста, нажмите на фрагмент кода выполнения и перетащите одно изображение для просмотра, оно покажет предварительный просмотр отброшенного изображения.
Дипак
6

Теоретически, вы можете добавить элемент, наложенный на <input/>, и затем использовать его dropсобытие для захвата файлов (используя File API) и передачи их во входной filesмассив.

За исключением того, что входной файл доступен только для чтения . Это старая проблема.

Однако вы можете полностью обойти контроль формы и загрузить его через XHR (не уверен насчет поддержки):

Вы также можете использовать элемент в окружающей области, чтобы отменить событие drop в Chrome и предотвратить поведение по умолчанию при загрузке файла.

Удаление нескольких файлов поверх ввода уже работает в Safari и Firefox.

Рикардо Томази
источник
6
Как я сказал в вопросе: я знаю XHR2 и не хочу его использовать. Полагаю, важная часть: «Ввод файла только для чтения». Это отстой ... Отмена события выпадения не плохая идея! Не так хорошо, как я надеялся, но, вероятно, лучше. Удаление нескольких файлов работает и в Chrome. Chrome теперь также позволяет загружать каталоги. Все очень непристойно и не помогает моему делу = (
Руди
5

Это то, что я вышел.

Использование Jquery и Html. Это добавит его в файлы вставки.

var dropzone = $('#dropzone')


dropzone.on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
    e.preventDefault();
    e.stopPropagation();
  })

dropzone.on('dragover dragenter', function() {
    $(this).addClass('is-dragover');
  })
dropzone.on('dragleave dragend drop', function() {
    $(this).removeClass('is-dragover');
  })  
  
dropzone.on('drop',function(e) {
	var files = e.originalEvent.dataTransfer.files;
	// Now select your file upload field 
	// $('input_field_file').prop('files',files)
  });
input {	margin: 15px 10px !important;}

.dropzone {
	padding: 50px;
	border: 2px dashed #060;
}

.dropzone.is-dragover {
  background-color: #e6ecef;
}

.dragover {
	bg-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class="" draggable='true' style='padding: 20px'>
	<div id='dropzone' class='dropzone'>
		Drop Your File Here
	</div>
	</div>

Лионель Йео
источник
4

Для решения только для CSS:

<div class="file-area">
    <input type="file">
    <div class="file-dummy">
        <span class="default">Click to select a file, or drag it here</span>
        <span class="success">Great, your file is selected</span>
    </div>
</div>

.file-area {
    width: 100%;
    position: relative;
    font-size: 18px;
}
.file-area input[type=file] {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0;
    cursor: pointer;
}
.file-area .file-dummy {
    width: 100%;
    padding: 50px 30px;
    border: 2px dashed #ccc;
    background-color: #fff;
    text-align: center;
    transition: background 0.3s ease-in-out;
}
.file-area .file-dummy .success {
    display: none;
}
.file-area:hover .file-dummy {
    border: 2px dashed #1abc9c;
}
.file-area input[type=file]:valid + .file-dummy {
    border-color: #1abc9c;
}
.file-area input[type=file]:valid + .file-dummy .success {
    display: inline-block;
}
.file-area input[type=file]:valid + .file-dummy .default {
    display: none;
}

Модифицировано с https://codepen.io/Scribblerockerz/pen/qdWzJw

Джонатан
источник
4

Я знаю некоторые хитрости в Chrome:

При перетаскивании файлов в зону перетаскивания вы получаете dataTransfer.filesобъект, то есть FileListтип объекта, который содержит все файлы, которые вы перетащили. Между тем, <input type="file" />элемент имеет свойство files, то есть FileListобъект того же типа.

Таким образом, вы можете просто присвоить dataTransfer.filesобъект input.filesсвойству.

Тимур Гилаури
источник
3
Да, это делает в эти дни. Не подвох. Очень намеренно Также очень намеренно очень ограничено. Вы не можете добавлять файлы в список или изменять его вообще. Перетаскивание может запоминать файлы и добавлять к ним, но input.filesне может = (
Rudie
3

Для тех, кто хочет сделать это в 2018 году, у меня есть намного лучшее и более простое решение, чем все старые вещи, размещенные здесь. Вы можете создать красивый перетаскивающий блок с простым HTML, JavaScript и CSS.

(Пока работает только в Chrome)

Давайте начнем с HTML.

<div>
<input type="file" name="file" id="file" class="file">
<span id="value"></span>
</div>

Тогда мы перейдем к стайлингу.

    .file {
        width: 400px;
        height: 50px;
        background: #171717;
        padding: 4px;
        border: 1px dashed #333;
        position: relative;
        cursor: pointer;
    }

    .file::before {
        content: '';
        position: absolute;
        background: #171717;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 100%;
        height: 100%;
    }

    .file::after {
        content: 'Drag & Drop';
        position: absolute;
        color: #808080;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }

После того, как вы это сделали, все уже выглядит хорошо. Но я думаю, что вы хотели бы посмотреть, какой файл вы загрузили на самом деле, поэтому мы собираемся сделать немного JavaScript. Помните этот диапазон значений pfp? Вот где мы распечатаем имя файла.

let file = document.getElementById('file');
file.addEventListener('change', function() {
    if(file && file.value) {
        let val = file.files[0].name;
        document.getElementById('value').innerHTML = "Selected" + val;
    }
});

И это все.

Майкл
источник
Я получаю Uncaught TypeError: Не могу прочитать свойство 'addEventListener' с нулевым значением, когда я использую этот код - в Chrome - он не работает в последних версиях Chrome?
Бой с огнем
Он отлично работает для меня в последней версии Chrome. Убедитесь, что вы используете правильные идентификаторы
Michael
1

Потрясающая работа @BjarkeCK. Я внес некоторые изменения в его работу, чтобы использовать его в качестве метода в jquery:

$.fn.dropZone = function() {
  var buttonId = "clickHere";
  var mouseOverClass = "mouse-over";

  var dropZone = this[0];
  var $dropZone = $(dropZone);
  var ooleft = $dropZone.offset().left;
  var ooright = $dropZone.outerWidth() + ooleft;
  var ootop = $dropZone.offset().top;
  var oobottom = $dropZone.outerHeight() + ootop;
  var inputFile = $dropZone.find("input[type='file']");
  dropZone.addEventListener("dragleave", function() {
    this.classList.remove(mouseOverClass);
  });
  dropZone.addEventListener("dragover", function(e) {
    console.dir(e);
    e.preventDefault();
    e.stopPropagation();
    this.classList.add(mouseOverClass);
    var x = e.pageX;
    var y = e.pageY;

    if (!(x < ooleft || x > ooright || y < ootop || y > oobottom)) {
      inputFile.offset({
        top: y - 15,
        left: x - 100
      });
    } else {
      inputFile.offset({
        top: -400,
        left: -400
      });
    }

  }, true);
  dropZone.addEventListener("drop", function(e) {
    this.classList.remove(mouseOverClass);
  }, true);
}

$('#drop-zone').dropZone();

Рабочая скрипка

Мистер Грин
источник
1
К вашему сведению: ссылка на скрипку не работает.
Джимайлер
1

Несколько лет спустя я построил эту библиотеку чтобы перетаскивать файлы в любой элемент HTML.

Вы можете использовать его как

const Droppable = require('droppable');

const droppable = new Droppable({
    element: document.querySelector('#my-droppable-element')
})

droppable.onFilesDropped((files) => {
    console.log('Files were dropped:', files);
});

// Clean up when you're done!
droppable.destroy();
Джоэл Эрнандес
источник
как можно получить выбранный файл позже при отправке формы?
Nikhil VJ
-1

Что вы можете сделать, это отобразить файл ввода и наложить его на прозрачную область перетаскивания, стараясь использовать имя вроде file[1]. {Убедитесь, что enctype="multipart/form-data"внутри вашего тега FORM.}

Затем дроп-область обрабатывает дополнительные файлы, динамически создавая дополнительные входные данные для файлов 2..number_of_files, обязательно используйте то же базовое имя, заполняя атрибут-значение соответствующим образом.

Наконец (передний конец) отправьте форму.


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

Shark8
источник
1
Файл ввода имеет multipleатрибут в эти дни. Нет необходимости вводить более 1 файла. Это не проблема, хотя. Как мне получить Fileобъекты в файл ввода? Я думаю, что это требует некоторого примера кода ...
Rudie
1
@ Руди, ты не можешь, вот в чем проблема.
Рикардо Томази
1
Не может что? Многократное? Да, ты можешь. Я только что сказал это. Несколько не проблема. Проблема в получении файлов из (перетаскиваемого) объекта File во входные данные файла.
Руди
@Rudie Получение перетаскиваемых файлов в файл ввода возможно с помощью Chrome / FF (с использованием filesсвойства), но в IE мне не удалось - вам повезло?
Jlb
1
@jlb Что вы подразумеваете под "использованием свойства files"? Не могли бы вы сделать ответ с соответствующим кодом? То, что я искал, не работает / не существует ни в одном браузере.
Руди