kéo tập tin thả vào đầu vào tập tin html tiêu chuẩn


163

Ngày nay, chúng ta có thể kéo và thả các tệp vào một thùng chứa đặc biệt và tải chúng lên bằng XHR 2. Nhiều lần. Với thanh tiến trình trực tiếp, vv Công cụ rất mát mẻ. Ví dụ ở đây.

Nhưng đôi khi chúng ta không muốn sự mát mẻ đó. Điều tôi muốn là kéo và thả các tệp - nhiều lần - vào một tệp HTML tiêu chuẩn : <input type=file multiple>.

Điều đó có thể không? Có cách nào để 'điền' đầu vào tệp với tên tệp đúng (?) Từ thả tệp không? (Các filepath đầy đủ không có sẵn vì lý do bảo mật hệ thống tệp.)

Tại sao? Bởi vì tôi muốn gửi một hình thức bình thường. Đối với tất cả các trình duyệt và tất cả các thiết bị. Kéo và thả chỉ là tăng cường lũy ​​tiến để tăng cường & đơn giản hóa UX. Biểu mẫu tiêu chuẩn với đầu vào tệp tiêu chuẩn ( multiplethuộc tính + ) sẽ ở đó. Tôi muốn thêm phần nâng cao HTML5.

chỉnh sửa
Tôi biết trong một số trình duyệt đôi khi bạn có thể (hầu như luôn luôn) thả tệp vào đầu vào tệp. Tôi biết Chrome thường làm điều này, nhưng đôi khi nó bị lỗi và sau đó tải tệp trong trang hiện tại (một thất bại lớn nếu bạn điền vào biểu mẫu). Tôi muốn đánh lừa- & trình duyệt nó.


1
Chuẩn bị cho một số đau đớn nếu bạn muốn bao gồm mac / safari trong khả năng tương thích của bạn.
Shark8

1
@ Shark8 thực sự Safari / Mac là một trong số ít các trình duyệt đã hỗ trợ này.
Ricardo Tomasi

Trên thực tế, không có trình duyệt nào hỗ trợ điều này. Trường nhập tệp là chỉ đọc (để bảo mật) và đó là vấn đề. Bảo mật ngu ngốc!
Rudie

2
Bằng cách này, tôi có nghĩa là "kéo và thả các tệp - nhiều lần - vào một tệp nhập HTML tiêu chuẩn".
Ricardo Tomasi

3
kéo / thả nhiều tệp để input type="file" multiplehoạt động tốt trong Safari
Lloyd

Câu trả lời:


71

Các hoạt động sau đây trong Chrome và FF, nhưng tôi vẫn chưa tìm thấy giải pháp bao gồm 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>

Bạn có thể muốn sử dụng addEventListenerhoặc jQuery (v.v.) để đăng ký trình xử lý evt của mình - đây chỉ là vì lợi ích của sự ngắn gọn.


3
Chờ đợi! Nó hoạt động!? Đó chính xác là những gì tôi đang tìm kiếm. Không làm việc 2 năm trước. Tuyệt vời! Tất nhiên nó không hoạt động trong IE =) Câu hỏi quan trọng: có phát hiện tính năng đáng tin cậy không?, Vì vậy bạn có thể ẩn dropzone trong IE, nhưng nó không hoạt động.
Rudie

Thôi, hơi muộn rồi :) Ngay bây giờ tôi chỉ sử dụng kiểm tra tác nhân người dùng đơn giản trong JS. Tất nhiên bạn phải kiểm tra MSIE , Trident/(IE11) và Edge/(IE12) ...
jlb

FF 48.0.2 (Mac) ném "TypeError: thiết lập một thuộc tính chỉ có một getter" tại dòng fileInput.files = evt.dataTransfer.files;. Tuy nhiên, Safari và Chrome đều hoạt động tốt.
Risadinha

2
Ví dụ này không hoạt động trên firefox 45 trên linux, nhưng nó hoạt động với tôi trên chrome. Tôi không nhận được bất kỳ lỗi nào trong bảng điều khiển, đơn giản là nó không hiển thị bất kỳ tệp nào bị bỏ.
Bryan Oakley

1
thực sự tôi đã làm một bài để thử và tìm một giải pháp nhưng tự mình tìm ra. Thay đổi khá đơn giản, chỉ cần fileInputs [index] = ... để truyền dữ liệu tệp đến một đầu vào cụ thể và sau đó gọi một hàm showNext để thêm một stackoverflowflow.com/a/43397640/6392779
nick

51

Tôi đã thực hiện một giải pháp cho việc này.

Chức năng Kéo và Thả cho phương pháp này chỉ hoạt động với Chrome, Firefox và Safari. (Không biết nó có hoạt động với IE10 không), nhưng đối với các trình duyệt khác, nút "Hoặc nhấp vào đây" hoạt động tốt.

Trường nhập chỉ cần theo chuột của bạn khi kéo tệp qua một khu vực và tôi cũng đã thêm một nút ..

Độ mờ đục không mong muốn: 0; đầu vào tập tin chỉ hiển thị để bạn có thể thấy những gì đang diễn ra.


Đó là lý do tại sao tôi đã thêm một nút nữa ^^ Nhưng bạn đúng rồi. Tôi sẽ không sử dụng nó ... hay tôi!?
BjarkeCK

Tôi ước tôi biết nó hoạt động như thế nào ... có vẻ như tất cả các chức năng kéo / thả phải xử lý bằng cách thêm hiệu ứng di chuột ... nhưng tôi thực sự không thể biết được. Có vẻ tốt trong câu đố, nhưng tôi không nghĩ rằng tôi có thể sử dụng nó vì tôi cần hỗ trợ Internet Explorer
nmz787

1
@PiotrKowalski Tôi nghĩ rằng điều đó có khả năng kích hoạt một cuộc gọi đệ quy cho đến khi ngăn xếp cuộc gọi tràn ra
John

2
Tôi cuối cùng chỉ sử dụng phong cách. Làm cho đầu vào 100% chiều rộng và chiều cao hoạt động tốt hơn so với di chuyển xung quanh.
Eddie

2
Có cách nào để thoát khỏi "không có tệp nào được chọn" mà vẫn di chuột cùng với con trỏ chuột của chúng tôi không? @BjarkeCK
Abhishek Singh

27

Đây là cách HTML5 "DTHML" để làm điều đó. Đầu vào dạng thông thường (mà IS chỉ đọc như Ricardo Tomasi đã chỉ ra). Sau đó, nếu một tệp được kéo vào, nó được đính kèm vào biểu mẫu. SILL này yêu cầu sửa đổi trang hành động để chấp nhận tệp được tải lên theo cách này.

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>

Thậm chí còn tuyệt vời hơn nếu bạn có thể biến toàn bộ cửa sổ thành một khu vực thả, xem Làm cách nào để phát hiện sự kiện kéo HTML5 vào và rời khỏi cửa sổ, giống như Gmail?


1
Giải pháp tốt nhưng nó không hoạt động trên IE <10 vì IE 9 và ít hơn không hỗ trợ API tệp API :(
Develoger

1
Dòng này: document.getEuityById ('fileDragData'). Value = files [i] .slice (); là không cần thiết, bởi vì nó được thay thế trong hàm
reader.onload

Đây là một ứng dụng kéo và thả dễ thương khác KHÔNG liên quan đến tải lên tệp. Liên kết chỉ trong trường hợp ai đó muốn nghiên cứu thêm. codepen.io/anon/pen/MOPvZK?editors=1010
William Entriken

1
Giải pháp IE 10 là làm giảm và chỉ hiển thịinput type=file
William Entriken

Tôi có thiếu thứ gì không, hay bạn chỉ liên tục ghi đè lên .valuetài sản bằng tệp gần đây nhất, mỗi khi bạn lặp qua vòng lặp trước?
Kevin Burke

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
Nó hiển thị gì cho người dùng? Bạn có thể làm một fiddle hoặc ví dụ trực tuyến?
Rudie

@Rudie vui lòng nhấp vào chạy đoạn mã và kéo-n-drop một hình ảnh để xem, nó sẽ hiển thị phần xem trước của hình ảnh bị rơi.
Dipak

6

Về lý thuyết, bạn có thể thêm một phần tử che phủ <input/>, và sau đó sử dụng dropsự kiện của nó để chụp các tệp (sử dụng API tệp) và chuyển chúng vào filesmảng đầu vào .

Ngoại trừ việc một tập tin đầu vào là chỉ đọc . Đây là một vấn đề cũ.

Tuy nhiên, bạn có thể bỏ qua hoàn toàn kiểm soát biểu mẫu và tải lên qua XHR (không chắc chắn về sự hỗ trợ cho điều đó):

Bạn cũng có thể sử dụng một yếu tố trong khu vực xung quanh để hủy sự kiện thả trong Chrome và ngăn hành vi tải tệp mặc định.

Bỏ nhiều tệp qua đầu vào đã hoạt động trong Safari và Firefox.


6
Giống như tôi đã nói trong câu hỏi: Tôi biết XHR2 và tôi không muốn sử dụng nó. Tôi đoán phần quan trọng: "đầu vào tệp là chỉ đọc". Thật tệ ... Hủy bỏ sự kiện thả không phải là một ý tưởng tồi! Không tốt như tôi mong đợi, nhưng có lẽ là tốt nhất. Bỏ nhiều tệp hoạt động trong Chrome quá btw. Chrome hiện cũng cho phép tải lên các thư mục. Tất cả đều rất kewl và không giúp ích gì cho trường hợp của tôi = (
Rudie

5

Đây là những gì tôi đi ra với.

Sử dụng Jquery và Html. Điều này sẽ thêm nó vào các tập tin chèn.

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

Đối với giải pháp chỉ 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;
}

Được sửa đổi từ https://codepen.io/Scripblerockerz/pen/qdWzJw


4

Tôi biết một số mẹo hoạt động trong Chrome:

Khi thả tệp vào vùng thả bạn nhận được một dataTransfer.filesđối tượng, đó là một FileListloại đối tượng, chứa tất cả các tệp bạn đã kéo. Trong khi đó, <input type="file" />phần tử có thuộc tính files, đó là FileListđối tượng cùng loại.

Vì vậy, bạn có thể chỉ cần gán dataTransfer.filesđối tượng cho thuộc input.filestính.


3
Vâng, nó làm những ngày này. Không phải là một mẹo. Rất cố ý. Cũng rất cố ý rất hạn chế. Bạn không thể thêm tệp vào danh sách hoặc thay đổi danh sách cả. Kéo và thả có thể nhớ các tệp và thêm vào chúng, nhưng input.fileskhông thể = (
Rudie

3

Đối với bất kỳ ai muốn làm điều này vào năm 2018, tôi đã có một giải pháp tốt hơn và đơn giản hơn sau đó tất cả những thứ cũ được đăng ở đây. Bạn có thể tạo một hộp kéo và thả đẹp mắt chỉ bằng vanilla HTML, JavaScript và CSS.

(Chỉ hoạt động trong Chrome cho đến nay)

Hãy bắt đầu với HTML.

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

Sau đó, chúng ta sẽ đi đến phong cách.

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

Sau khi bạn thực hiện điều này, nó đã có vẻ tốt. Nhưng tôi tưởng tượng bạn muốn xem tập tin nào bạn đã tải lên một cách chính xác, vì vậy chúng tôi sẽ thực hiện một số JavaScript. Hãy nhớ rằng khoảng giá trị pfp? Đó là nơi chúng tôi sẽ in tên tập tin.

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

Và đó là nó.


Tôi nhận được một Uncaught TypeError: Không thể đọc thuộc tính 'addEventListener' của null khi tôi sử dụng mã này - trong Chrome - nó không hoạt động trong các phiên bản Chrome mới nhất phải không?
Chiến đấu với lửa

Nó hoạt động tốt với tôi trong phiên bản Chrome mới nhất. Đảm bảo bạn sử dụng đúng ID
Michael

1

Công việc tuyệt vời của @BjarkeCK. Tôi đã thực hiện một số sửa đổi cho công việc của mình, để sử dụng nó làm phương pháp trong 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();

Fiddle làm việc


1
FYI: Liên kết Fiddle bị hỏng.
jimiayler

1

Vài năm sau, tôi đã xây dựng thư viện này để thả các tệp vào bất kỳ phần tử HTML nào.

Bạn có thể sử dụng nó như

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

Làm thế nào để tìm nạp tệp đã chọn sau này khi gửi biểu mẫu?
Nikhil VJ

-1

Những gì bạn có thể làm, là hiển thị một đầu vào tệp và phủ nó với vùng thả trong suốt của bạn, cẩn thận sử dụng tên như thế nào file[1]. {Hãy chắc chắn có enctype="multipart/form-data"thẻ FORM của bạn.}

Sau đó, khu vực thả xử lý các tệp bổ sung bằng cách tự động tạo thêm đầu vào tệp cho tệp 2..number_of_files, đảm bảo sử dụng cùng tên cơ sở, điền vào thuộc tính giá trị một cách thích hợp.

Cuối cùng (front-end) gửi mẫu.


Tất cả những gì cần thiết để xử lý phương pháp này là thay đổi quy trình của bạn để xử lý một mảng các tệp.


1
Các đầu vào tập tin có một multiplethuộc tính những ngày này. Không cần nhiều hơn 1 tập tin đầu vào. Đó không phải là vấn đề mặc dù. Làm thế nào để tôi có được các Fileđối tượng vào đầu vào tập tin? Tôi nghĩ rằng điều này đòi hỏi một số ví dụ mã ...
Rudie

1
@Rudie bạn không thể, đó là vấn đề.
Ricardo Tomasi

1
Không thể làm gì? Bội số? Vâng, bạn có thể. Tôi chỉ nói rằng. Nhiều không phải là vấn đề. Việc nhận các tệp từ một đối tượng Tệp (được kéo) vào đầu vào tệp, đó là vấn đề.
Rudie

@Rudie có thể kéo (các) tệp vào đầu vào tệp với Chrome / FF (sử dụng thuộc filestính), nhưng tôi chưa quản lý được trong IE - bạn có gặp may không?
jlb

1
@jlb Ý bạn là gì "sử dụng thuộc tính tệp"? Bạn có thể đưa ra một câu trả lời với mã có liên quan? Những gì tôi đang tìm kiếm không hoạt động / tồn tại trong bất kỳ trình duyệt.
Rudie
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.