przeciągnij i upuść pliki do standardowego wejścia pliku html

163

Obecnie możemy przeciągać i upuszczać pliki do specjalnego kontenera i przesyłać je za pomocą XHR 2. Wiele na raz. Z paskami postępu na żywo itp. Bardzo fajne rzeczy. Przykład tutaj.

Ale czasami nie chcemy tyle chłodu. Co chciałbym to przeciągnij i upuść pliki - wiele naraz - do standardowego wejścia plik HTML : <input type=file multiple>.

Czy to jest możliwe? Czy istnieje sposób na „wypełnienie” wejścia pliku odpowiednimi nazwami plików (?) Z upuszczenia pliku? (Pełne ścieżki plików nie są dostępne ze względów bezpieczeństwa systemu plików).

Czemu? Ponieważ chciałbym przesłać zwykły formularz. Dla wszystkich przeglądarek i wszystkich urządzeń. Przeciąganie i upuszczanie to po prostu stopniowe ulepszenie w celu ulepszenia i uproszczenia UX. multipleBędzie tam standardowy formularz ze standardowym wejściem do pliku (+ atrybut). Chciałbym dodać rozszerzenie HTML5.

edytuj
Wiem, że w niektórych przeglądarkach możesz czasami (prawie zawsze) upuszczać pliki do samego wejścia. Wiem, że Chrome zwykle to robi, ale czasami kończy się to niepowodzeniem, a następnie ładuje plik na bieżącej stronie (duży błąd, jeśli wypełniasz formularz). Chcę, aby był odporny na błędy i przeglądarki.

Rudie
źródło
1
Przygotuj się na ból, jeśli chcesz uwzględnić mac / safari w swoich kompatybilnościach.
Shark 8
1
@ Shark8 faktycznie Safari / Mac jest jedną z niewielu przeglądarek, które już to obsługują.
Ricardo Tomasi
Właściwie żadna z przeglądarek tego nie obsługuje. Pole wejściowe pliku jest tylko do odczytu (ze względów bezpieczeństwa) i to jest problem. Głupia ochrona!
Rudie
2
Przez to miałem na myśli „przeciągnij i upuść pliki - wiele naraz - do standardowego wejścia plik HTML”.
Ricardo Tomasi
3
przeciągnij / upuść wiele plików, aby input type="file" multipledziałały dobrze w Safari
Lloyd

Odpowiedzi:

71

Poniższe działa w Chrome i FF, ale nie znalazłem jeszcze rozwiązania, które obejmuje również 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>

Prawdopodobnie będziesz chciał użyć addEventListenerlub jQuery (itp.), Aby zarejestrować swoje programy obsługi evt - to jest tylko dla zwięzłości.

jlb
źródło
3
Waaaaaaaat! To działa!? To jest dokładnie to, czego szukałem. Nie działał 2 lata temu. Niesamowite! Oczywiście to nie działa w IE =) Ważne pytanie: czy istnieje niezawodne wykrywanie funkcji ?, więc możesz ukryć strefę zrzutu w IE, bo to nie zadziała.
Rudie
Aha, trochę za późno :) W tej chwili używam tylko prostych testów agenta użytkownika w JS. Oczywiście musisz przetestować MSIE , Trident/(IE11) i Edge/(IE12) ...
jlb
FF 48.0.2 (Mac) generuje „TypeError: ustawienie właściwości, która ma tylko metodę pobierającą” w wierszu fileInput.files = evt.dataTransfer.files;. Safari i Chrome działają jednak dobrze.
Risadinha
2
Ten przykład nie działa w przeglądarce Firefox 45 na Linuksie, ale działa dla mnie na Chrome. Nie dostaję żadnych błędów konsoli, po prostu nie pokazuje, że jakikolwiek plik został upuszczony.
Bryan Oakley
1
właściwie napisałem post, aby spróbować znaleźć rozwiązanie, ale sam się zorientowałem. Całkiem prosta zmiana, po prostu fileInputs [index] = ... aby przekazać dane pliku do określonego wejścia, a następnie wywołaj funkcję showNext w celu dodania nowego wejścia stackoverflow.com/a/43397640/6392779
nick
51

Zrobiłem na to rozwiązanie.

Funkcja „przeciągnij i upuść” dla tej metody działa tylko w przeglądarkach Chrome, Firefox i Safari. (Nie wiem, czy działa z IE10), ale w przypadku innych przeglądarek przycisk „Lub kliknij tutaj” działa dobrze.

Pole wprowadzania danych po prostu podążaj za myszą podczas przeciągania pliku po obszarze. Dodałem również przycisk.

Nieprzezroczystość odkomentowania: 0; wejście pliku jest widoczne tylko po to, abyś mógł zobaczyć, co się dzieje.

BjarkeCK
źródło
Dlatego też dodałem przycisk ^^ Ale tak, masz rację. Nie użyłbym tego ze skóry ... A może bym !?
BjarkeCK
Chciałbym wiedzieć, jak to ma działać ... wydaje się, że wszystkie funkcje przeciągania / upuszczania mają do czynienia z dodaniem efektu najechania ... ale naprawdę nie mogę powiedzieć. Wygląda dobrze na skrzypcach, ale nie sądzę, żebym mógł go używać, ponieważ muszę obsługiwać Internet Explorera
nmz787
1
@PiotrKowalski Myślę, że to potencjalnie wywołałoby wywołanie rekurencyjne do czasu przepełnienia stosu wywołań
John
2
Skończyło się na tym, że użyłem tylko stylu. Wprowadzenie 100% szerokości i wysokości działało lepiej niż przesuwanie go.
Eddie
2
Czy istnieje sposób, aby pozbyć się „nie wybrano pliku”, który ciągle pojawia się wraz z naszym wskaźnikiem myszy? @BjarkeCK
Abhishek Singh
27

To jest sposób na zrobienie tego w HTML5 „DTHML”. Normalne dane wejściowe (które JEST czytane tylko, jak wskazał Ricardo Tomasi). Następnie, jeśli plik jest przeciągany, jest dołączany do formularza. Będzie to wymagało modyfikacji strony akcji, aby zaakceptować przesłany w ten sposób plik.

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>

Jeszcze bardziej szefowe jest, jeśli możesz uczynić całe okno strefą upuszczania, zobacz Jak mogę wykryć zdarzenie przeciągania HTML5 wchodzące i wychodzące z okna, tak jak robi to Gmail?

William Entriken
źródło
1
Dobre rozwiązanie, ale nie działa na IE <10, ponieważ IE 9 i mniej nie obsługują API plików HTML5 :(
Develoger
1
Ta linia: document.getElementById ('fileDragData'). Value = files [i] .slice (); nie jest potrzebny, ponieważ jest zastąpiony w czytniku. funkcja
ładowania
Oto kolejna urocza aplikacja typu „przeciągnij i upuść”, która NIE wymaga przesyłania plików. Łączenie na wypadek, gdyby ktoś chciał się więcej uczyć. codepen.io/anon/pen/MOPvZK?editors=1010
William Entriken
1
Rozwiązanie IE 10 polega na degradacji i wyświetlaniuinput type=file
William Entriken
Czy coś mi brakuje, czy po prostu ciągle nadpisujesz .valuewłaściwość najnowszym plikiem, za każdym razem, gdy przechodzisz przez pętlę fore?
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>

Dipak
źródło
2
Co to pokazuje użytkownikowi? Czy potrafisz zrobić przykład ze skrzypiec lub online?
Rudie
@Rudie, kliknij uruchom fragment kodu i przeciągnij i upuść jeden obraz, aby wyświetlić, wyświetli się podgląd upuszczonego obrazu.
Dipak
6

Teoretycznie można dodać element nakładający się na <input/>, a następnie użyć jego dropzdarzenia do przechwycenia plików (za pomocą interfejsu File API) i przekazania ich do filestablicy wejściowej .

Tyle tylko, że plik wejściowy jest tylko do odczytu . To stary problem.

Możesz jednak całkowicie ominąć kontrolę formularza i przesłać go przez XHR (nie jestem pewien, czy jest to obsługiwane):

Możesz również użyć elementu w okolicy, aby anulować zdarzenie upuszczenia w Chrome i zapobiec domyślnemu zachowaniu ładowania pliku.

Upuszczanie wielu plików na dane wejściowe już działa w Safari i Firefox.

Ricardo Tomasi
źródło
6
Jak powiedziałem w pytaniu: znam XHR2 i nie chcę go używać. Myślę, że najważniejsza część: „plik wejściowy jest tylko do odczytu”. To jest do bani ... Anulowanie dropu nie jest złym pomysłem! Nie tak dobre, jak się spodziewałem, ale prawdopodobnie najlepsze. Upuszczanie wielu plików działa też w Chrome. Chrome umożliwia teraz także przesyłanie katalogów. Wszystko bardzo kewl i nie pomaga mojej sprawie = (
Rudie
5

Oto, z czym wyszedłem.

Korzystanie z Jquery i Html. Spowoduje to dodanie go do plików wstawiania.

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>

Lionel Yeo
źródło
4

Tylko rozwiązanie 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;
}

Zmodyfikowano z https://codepen.io/Scribblerockerz/pen/qdWzJw

Jonathan
źródło
4

Wiem, że w Chrome działa pewna sztuczka:

Podczas upuszczania plików do strefy upuszczania otrzymujesz dataTransfer.filesobiekt, czyli FileListtyp obiektu, który zawiera wszystkie przeciągnięte pliki. Tymczasem <input type="file" />element ma właściwość files, czyli obiekt tego samego FileListtypu.

Możesz więc po prostu przypisać dataTransfer.filesobiekt do input.fileswłaściwości.

Timur Gilauri
źródło
3
Tak, obecnie tak. To nie jest sztuczka. Bardzo celowo. Również bardzo celowo bardzo ograniczone. Nie możesz dodawać plików do listy ani w ogóle modyfikować listy. Przeciąganie i upuszczanie może zapamiętywać pliki i dodawać do nich, ale input.filesnie może = (
Rudie,
3

Dla każdego, kto chce to zrobić w 2018 r., Mam znacznie lepsze i prostsze rozwiązanie niż wszystkie stare rzeczy opublikowane tutaj. Możesz stworzyć ładnie wyglądające pole typu „przeciągnij i upuść” za pomocą samego czystego kodu HTML, JavaScript i CSS.

(Jak dotąd działa tylko w Chrome)

Zacznijmy od HTML.

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

Następnie przejdziemy do stylizacji.

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

Po wykonaniu tej czynności już wygląda dobrze. Ale wyobrażam sobie, że chciałbyś zobaczyć, jaki plik faktycznie przesłałeś, więc zrobimy trochę JavaScript. Pamiętasz ten zakres wartości pfp? Tam wydrukujemy nazwę pliku.

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

I to wszystko.

Michael
źródło
Otrzymuję Uncaught TypeError: Nie mogę odczytać właściwości „addEventListener” o wartości null, gdy używam tego kodu - w przeglądarce Chrome - czy nie działa w najnowszych wersjach Chrome?
Zwalcz ogień ogniem,
U mnie działa dobrze w najnowszej wersji Chrome. Upewnij się, że używasz właściwych identyfikatorów
Michael
1

Niesamowita praca @BjarkeCK. Dokonałem pewnych modyfikacji w jego pracy, aby użyć jej jako metody w 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();

Working Fiddle

Pan Zielony
źródło
1
FYI: Link do Fiddle jest uszkodzony.
jimiayler
1

Kilka lat później zbudowałem tę bibliotekę, aby upuszczać pliki do dowolnego elementu HTML.

Możesz go używać jak

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();
Joel Hernandez
źródło
jak pobrać wybrany plik później przy wysyłaniu formularza?
Nikhil VJ
-1

To, co możesz zrobić, to wyświetlić plik wejściowy i nałożyć na niego przezroczysty obszar upuszczania, uważając, aby użyć nazwy takiej jak file[1]. {Upewnij się, że enctype="multipart/form-data"tag FORMULARZ znajduje się wewnątrz.}

Następnie niech obszar upuszczania obsłuży dodatkowe pliki, dynamicznie tworząc więcej plików wejściowych dla plików 2..number_of_files, upewnij się, że używasz tej samej nazwy podstawowej, odpowiednio wypełniając atrybut wartość.

Na koniec (front-end) prześlij formularz.


Wszystko, co jest wymagane do obsługi tej metody, to zmiana procedury do obsługi tablicy plików.

Rekin8
źródło
1
Plik wejściowy ma obecnie multipleatrybut. Nie potrzeba więcej niż 1 pliku wejściowego. Ale to nie jest problem. Jak wprowadzić Fileobiekty do pliku wejściowego? Myślę, że to wymaga przykładu kodu ...
Rudie
1
@Rudie nie możesz, to jest problem.
Ricardo Tomasi
1
Nie możesz co? Wielokrotność? Tak, możesz. Właśnie to powiedziałem. Wielokrotność nie jest problemem. Pobieranie plików z (przeciągniętego) obiektu File do pliku wejściowego, to jest problem.
Rudie
@Rudie przeciąganie plików do pliku wejściowego jest możliwe za pomocą Chrome / FF (przy użyciu fileswłaściwości), ale nie udało mi się w IE - czy miałeś szczęście?
jlb
1
@jlb Co masz na myśli „używając właściwości files”? Czy mógłbyś udzielić odpowiedzi za pomocą odpowiedniego kodu? To, czego szukałem, nie działa / nie istnieje w żadnej przeglądarce.
Rudie