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

131

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

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

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

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

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

  • 1
    Приготовьтесь к некоторой боли, если вы хотите включить mac / safari в вашу совместимость.
  • 1
    @ Shark8 на самом деле Safari / Mac - один из немногих браузеров, уже поддерживающих это.
Показать ещё 5 комментариев
Теги:
file-upload
drag-and-drop

12 ответов

44
Лучший ответ

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

$(function () {
    var dropZoneId = "drop-zone";
    var buttonId = "clickHere";
    var mouseOverClass = "mouse-over";

    var dropZone = $("#" + dropZoneId);
    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");
    document.getElementById(dropZoneId).addEventListener("dragover", function (e) {
        e.preventDefault();
        e.stopPropagation();
        dropZone.addClass(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);

    if (buttonId != "") {
        var clickZone = $("#" + buttonId);

        var oleft = clickZone.offset().left;
        var oright = clickZone.outerWidth() + oleft;
        var otop = clickZone.offset().top;
        var obottom = clickZone.outerHeight() + otop;

        $("#" + buttonId).mousemove(function (e) {
            var x = e.pageX;
            var y = e.pageY;
            if (!(x < oleft || x > oright || y < otop || y > obottom)) {
                inputFile.offset({ top: y - 15, left: x - 160 });
            } else {
                inputFile.offset({ top: -400, left: -400 });
            }
        });
    }

    document.getElementById(dropZoneId).addEventListener("drop", function (e) {
        $("#" + dropZoneId).removeClass(mouseOverClass);
    }, true);

})
#drop-zone {
    /*Sort of important*/
    width: 300px;
    /*Sort of important*/
    height: 200px;
    position:absolute;
    left:50%;
    top:100px;
    margin-left:-150px;
    border: 2px dashed rgba(0,0,0,.3);
    border-radius: 20px;
    font-family: Arial;
    text-align: center;
    position: relative;
    line-height: 180px;
    font-size: 20px;
    color: rgba(0,0,0,.3);
}

    #drop-zone input {
        /*Important*/
        position: absolute;
        /*Important*/
        cursor: pointer;
        left: 0px;
        top: 0px;
        /*Important This is only comment out for demonstration purposes.
        opacity:0; */
    }

    /*Important*/
    #drop-zone.mouse-over {
        border: 2px dashed rgba(0,0,0,.5);
        color: rgba(0,0,0,.5);
    }


/*If you dont want the button*/
#clickHere {
    position: absolute;
    cursor: pointer;
    left: 50%;
    top: 50%;
    margin-left: -50px;
    margin-top: 20px;
    line-height: 26px;
    color: white;
    font-size: 12px;
    width: 100px;
    height: 26px;
    border-radius: 4px;
    background-color: #3b85c3;

}

    #clickHere:hover {
        background-color: #4499DD;

    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="drop-zone">
    Drop files here...
    <div id="clickHere">
        or click here..
        <input type="file" name="file" id="file" />
    </div>
</div>

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

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

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

  • 0
    Вот почему я добавил кнопку также ^^ Но да, ты прав. Я бы не стал пользоваться ею ... Или я?
  • 0
    Хотел бы я знать, как это должно работать ... кажется, что все функции перетаскивания должны иметь дело с добавлением эффекта наведения ... но я действительно не могу сказать. Выглядит хорошо в скрипке, но я не думаю, что смогу использовать его, так как мне нужно поддерживать Internet Explorer
Показать ещё 9 комментариев
34

В 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;
  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 - это просто ради краткости.

  • 2
    Waaaaaaaat! Это работает!? Это именно то , что я искал. Не работал 2 года назад. Потрясающие! Конечно, это не работает в IE =) Важный вопрос: есть ли надежное обнаружение функции ?, так что вы можете скрыть рабочую зону в IE, потому что она не будет работать.
  • 0
    Да, немного поздно :) Сейчас я просто использую простые проверки пользовательских агентов в JS. Конечно, вы должны проверить MSIE , Trident/ (IE11) и Edge/ (IE12) ...
Показать ещё 9 комментариев
27

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

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 :(
  • 0
    Это не делает настоящую загрузку файла ...
Показать ещё 5 комментариев
8

//----------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:180px; 
    border: 10px dashed #ccc;
}

#holder.hover { 
    width:400px; 
    height:180px; 
    border: 10px 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="500" height="180" style="width:500px; height:180px; border: 10px dashed #7A97FC;" class=" hidden"/>
        </div>
      </form>
    </body>
</html>
  • 2
    Что это показывает пользователю? Можете ли вы сделать скрипку или онлайн пример?
  • 0
    @Rudie, пожалуйста, нажмите фрагмент кода запуска и перетащите одно изображение для просмотра, оно покажет предварительный просмотр отброшенного изображения.
Показать ещё 1 комментарий
6

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

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

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

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

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

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

Я знаю, что в Chrome работает трюк.

При отбрасывании файлов в зону перетаскивания вы получаете объект dataTransfer.files, который является объектом типа FileList, который содержит все файлы, которые вы перетаскивали. Между тем элемент имеет свойство "файлы", то есть тот же объект типа "FileList".

Итак, вы можете просто определить объект dataTransfer.files для свойства input.files.

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

Для решения только 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

1

Для тех, кто хочет сделать это в 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;
    }
});

И это оно.

  • 0
    Я получаю Uncaught TypeError: Не могу прочитать свойство 'addEventListener' с нулевым значением, когда я использую этот код - в Chrome - он не работает в последних версиях Chrome?
  • 0
    Он отлично работает для меня в последней версии Chrome. Убедитесь, что вы используете правильные идентификаторы
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();
  • 0
    Не работает с IE 11, хотя.
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();

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

  • 0
    К вашему сведению: ссылка на скрипку не работает.
0

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

Использование 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>
0

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

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

Наконец, (front-end) отправьте форму.


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

  • 1
    Файл ввода имеет multiple атрибутов в наши дни. Нет необходимости вводить более 1 файла. Это не проблема, хотя. Как получить объекты File в файл ввода? Я думаю, что это требует некоторого примера кода ...
  • 1
    @ Руди, ты не можешь, вот в чем проблема.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню