arraste os arquivos soltos para a entrada de arquivo html padrão

163

Atualmente, podemos arrastar e soltar arquivos em um contêiner especial e carregá-los com o XHR 2. Muitos de cada vez. Com barras de progresso ao vivo, etc. Coisas muito legais. Exemplo aqui.

Mas às vezes não queremos tanta frescura. O que eu gostaria é para arquivos de drag & drop - muitos de cada vez - em uma entrada de arquivo HTML padrão : <input type=file multiple>.

Isso é possível? Existe alguma maneira de 'preencher' a entrada do arquivo com os nomes de arquivos corretos (?) Na lista de opções? (Os caminhos de arquivo completos não estão disponíveis por razões de segurança do sistema de arquivos.)

Por quê? Porque eu gostaria de enviar um formulário normal. Para todos os navegadores e todos os dispositivos. O arrastar e soltar é apenas um aprimoramento progressivo para aprimorar e simplificar o UX. O formulário padrão com entrada de arquivo padrão ( multipleatributo + ) estará lá. Eu gostaria de adicionar o aprimoramento do HTML5.

edit
Eu sei que em alguns navegadores você pode, às vezes (quase sempre) soltar arquivos na própria entrada do arquivo. Eu sei que o Chrome geralmente faz isso, mas às vezes falha e carrega o arquivo na página atual (uma grande falha se você estiver preenchendo um formulário). Eu quero enganar e à prova de navegador.

Rudie
fonte
1
Prepare-se para um pouco de dor se quiser incluir o mac / safari em suas compatibilidades.
Shark8
1
@ Shark8, na verdade, o Safari / Mac é um dos poucos navegadores que já suportam isso.
Ricardo Tomasi
Na verdade, nenhum dos navegadores suporta isso. O campo de entrada do arquivo é somente leitura (por segurança) e esse é o problema. Segurança estúpida!
quer
2
Com isso, eu quis dizer "arrastar e soltar arquivos - muitos de cada vez - em uma entrada de arquivo HTML padrão".
Ricardo Tomasi
3
arrastar / soltar vários arquivos para input type="file" multipleobras bem em Safari
Lloyd

Respostas:

71

O seguinte funciona no Chrome e no FF, mas ainda não encontrei uma solução que cubra o 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>

Você provavelmente desejará usar o addEventListenerjQuery (etc.) para registrar seus manipuladores evt - isso é apenas por uma questão de brevidade.

jlb
fonte
3
Waaaaaaaat! Isso funciona!? Era exatamente o que eu estava procurando. Não funcionou há 2 anos. Impressionante! É claro que não funciona no IE =) A questão importante: existe uma detecção confiável de recursos?
Rudie
Ah, um pouco tarde então :) No momento, estou apenas usando verificações simples de agente de usuário em JS. Claro que você tem para testar MSIE , Trident/(IE11) e Edge/(IE12) ...
JLB
FF 48.0.2 (Mac) lança "TypeError: configurando uma propriedade que possui apenas um getter" na linha fileInput.files = evt.dataTransfer.files;. Safari e Chrome, no entanto, ambos funcionam bem.
Risadinha 26/10
2
Este exemplo não funciona no firefox 45 no linux, mas funciona para mim no chrome. Não recebo nenhum erro do console, simplesmente não mostra que nenhum arquivo foi descartado.
Bryan Oakley
1
na verdade, fiz um post para tentar encontrar uma solução, mas descobri por mim mesmo. Alteração bastante simples, basta fileInputs [index] = ... para passar os dados do arquivo para uma entrada específica e, em seguida, chamar uma função showNext para adicionar uma nova entrada stackoverflow.com/a/43397640/6392779
nick
51

Eu fiz uma solução para isso.

A funcionalidade de arrastar e soltar para esse método funciona apenas com Chrome, Firefox e Safari. (Não sei se funciona com o IE10), mas para outros navegadores, o botão "Ou clique aqui" funciona bem.

O campo de entrada simplesmente segue o mouse ao arrastar um arquivo sobre uma área, e eu adicionei um botão também ..

Opacidade não comentada: 0; a entrada do arquivo é visível apenas para que você possa ver o que está acontecendo.

BjarkeCK
fonte
É por isso que eu adicionei um botão também ^^ Mas você está certo. Eu não o usaria ... Ou eu usaria!?
BjarkeCK
Eu gostaria de saber como isso deve funcionar ... parece que todas as funções de arrastar / soltar têm a ver com a adição do efeito hover ... mas eu realmente não sei dizer. Parece bom no violino, mas eu não acho que eu posso usá-lo desde que eu preciso para suportar o Internet Explorer
nmz787
1
@PiotrKowalski Eu acho que seria potencialmente desencadear uma chamada recursiva até que a pilha de chamadas transborda
John
2
Acabei usando apenas o estilo. Tornar a entrada 100% de largura e altura funcionou melhor do que movê-la.
Eddie
2
Existe uma maneira de se livrar do "nenhum arquivo escolhido" que fica pairando junto com o ponteiro do mouse? @BjarkeCK
Abhishek Singh
27

Esta é a maneira HTML5 "DTHML" de fazer isso. Entrada de formulário normal (que é lida apenas como Ricardo Tomasi apontou). Se um arquivo for arrastado, ele será anexado ao formulário. Isso exigirá modificação da página de ação para aceitar o arquivo enviado desta maneira.

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>

É ainda mais importante se você pode tornar a janela inteira uma zona de soltar, consulte Como detecto um evento de arrastar HTML5 entrando e saindo da janela, como o Gmail?

William Entriken
fonte
1
Boa solução ainda não funciona no IE <10 porque o IE 9 e menos não suporta a API de arquivos HTML5 :(
Develoger
1
Esta linha: document.getElementById ('fileDragData'). Value = files [i] .slice (); não é necessário, porque ele é substituído na função reader.onload
kurdtpage
Aqui está outro aplicativo atraente de arrastar e soltar que NÃO envolve uploads de arquivos. Vinculação apenas no caso de alguém querer estudar mais. codepen.io/anon/pen/MOPvZK?editors=1010
William Entriken
1
A solução do IE 10 é degradar e mostrar apenas oinput type=file
William Entriken 29/11
Estou faltando alguma coisa, ou você simplesmente sobrescreve constantemente a .valuepropriedade com o arquivo mais recente, toda vez que itera pelo loop frontal?
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
fonte
2
O que mostra ao usuário? Você pode fazer um violino ou exemplo online?
Rudie
@ Rudie, por favor, clique em executar o snippet de código e arraste e solte uma imagem para ver, ela exibirá a visualização da imagem descartada.
Dipak
6

Em teoria, você pode adicionar um elemento sobrepondo-o <input/>e, em seguida, usá-lo droppara capturar os arquivos (usando a API de arquivos) e passá-los para a filesmatriz de entrada .

Exceto que uma entrada de arquivo é somente leitura . Este é um problema antigo.

No entanto, você pode ignorar completamente o controle de formulário e fazer o upload via XHR (não tem certeza sobre o suporte para isso):

Você também pode usar um elemento na área circundante para cancelar o evento de queda no Chrome e impedir o comportamento padrão de carregar o arquivo.

Soltar vários arquivos sobre a entrada já funciona no Safari e Firefox.

Ricardo Tomasi
fonte
6
Como eu disse na pergunta: conheço o XHR2 e não quero usá-lo. Eu acho que a parte iportant: "a entrada do arquivo é somente leitura". Isso é péssimo ... Cancelar o evento drop não é uma má ideia! Não é tão bom quanto eu esperava, mas provavelmente o melhor. Soltar vários arquivos também funciona no Chrome. O Chrome agora também permite o upload de diretórios. Tudo muito torto e não ajudando o meu caso = (
Rudie
5

Foi com isso que eu saí.

Usando Jquery e Html. Isso o adicionará aos arquivos de inserção.

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
fonte
4

Para uma solução apenas 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;
}

Modificado de https://codepen.io/Scribblerockerz/pen/qdWzJw

Jonathan
fonte
4

Eu sei que alguns truques funcionam no Chrome:

Ao soltar arquivos na zona de soltar, você obtém um dataTransfer.filesobjeto, que é um FileListtipo de objeto, que contém todos os arquivos que você arrastou. Enquanto isso, o <input type="file" />elemento tem a propriedade files, que é o mesmo FileListobjeto de tipo.

Portanto, você pode simplesmente atribuir o dataTransfer.filesobjeto à input.filespropriedade

Timur Gilauri
fonte
3
Sim, faz hoje em dia. Não é um truque. Muito intencional. Também é muito intencionalmente muito restrito. Você não pode adicionar arquivos à lista ou alterar a lista. Arrastando e soltando consigo lembrar arquivos, e adicionar para eles, mas input.filesnão posso = (
Rudie
3

Para quem quer fazer isso em 2018, tenho uma solução muito melhor e mais simples do que todas as coisas antigas postadas aqui. Você pode criar uma bela caixa de arrastar e soltar com apenas baunilha HTML, JavaScript e CSS.

(Funciona apenas no Chrome até o momento)

Vamos começar com o HTML.

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

Então, vamos chegar ao estilo.

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

Depois de fazer isso, ele já parece bem. Mas eu imagino que você gostaria de ver qual arquivo você carregou completamente, então vamos fazer um pouco de JavaScript. Lembra-se do período de valor pfp? É aí que imprimiremos o nome do arquivo.

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

E é isso.

Michael
fonte
Recebo um TypeError não capturado: Não é possível ler a propriedade 'addEventListener' nula quando uso esse código - no Chrome - ele não funciona nas versões mais recentes do Chrome?
Fight Fire With Fire
Funciona bem para mim na versão mais recente do Chrome. Certifique-se de usar os IDs corretos
Michael
1

Trabalho impressionante por @BjarkeCK. Fiz algumas modificações no seu trabalho, para usá-lo como método no 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();

Violino de trabalho

Senhor Verde
fonte
1
FYI: O link do violino está quebrado.
jimiayler
1

Poucos anos depois, construí esta biblioteca para soltar arquivos em qualquer elemento HTML.

Você pode usá-lo como

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
fonte
como buscar o arquivo selecionado posteriormente ao enviar o formulário?
Nikhil VJ 14/06
-1

O que você pode fazer é exibir uma entrada de arquivo e cobri-la com sua área de soltar transparente, tomando cuidado para usar um nome como file[1]. {Certifique-se de ter enctype="multipart/form-data"dentro da sua etiqueta FORM.}

Em seguida, faça com que a área de descarte lide com os arquivos extras, criando dinamicamente mais entradas de arquivos para os arquivos 2 .. número_de_arquivos, certifique-se de usar o mesmo nome base, preenchendo o atributo de valor adequadamente.

Por fim (front-end), envie o formulário.


Tudo o que é necessário para lidar com esse método é alterar seu procedimento para lidar com uma matriz de arquivos.

Shark8
fonte
1
A entrada do arquivo tem um multipleatributo atualmente. Não há necessidade de mais de 1 entrada de arquivo. Essa não é a questão. Como obtenho os Fileobjetos na entrada do arquivo? Eu estou pensando que isso requer algum exemplo de código ...
Rudie
1
@ Rudie você não pode, esse é o problema.
Ricardo Tomasi
1
Não pode o que? Múltiplo? Sim você pode. Acabei de dizer isso. O múltiplo não é o problema. Obter os arquivos de um objeto File (arrastado) em uma entrada de arquivo, é esse o problema.
Rudie
@Rudie obter arquivos arrastados para uma entrada de arquivo é possível com o Chrome / FF (usando a filespropriedade), mas eu não consegui no IE - você teve alguma sorte?
JLB
1
@jlb O que você quer dizer com "usando a propriedade files"? Você poderia responder com código relevante? O que eu estava procurando não funciona / existe em nenhum navegador.
quer