如何使 react-dropzone 中的预览与图像以外的文件一起使用?

fad*_*dah 8 javascript upload preview reactjs react-dropzone

我需要有关如何制作react-dropzone NPM 包的帮助,以便让上传的文件在图像文件(*.png、*.jpg/jpeg、*.gif 等)以外的文件上显示预览——这些都只生成预览美好的)。

Currently, when I use Dropzone for uploading accompanying files on a web form, if I Upload an Image file (*.png, *.jpg, etc.), the Preview we have set up shows just fine with a small thumbnail of the Uploaded file. (see pic below)

However, if I upload another type of file, say an MS-Outlook *.docx, *.xlsx, or say an Adobe Acrobat *.pdf, it just gives me a blank box with a broken file image and whatever alt="..." text I had put in there, in this case, "Uploaded File Preview." (see pic below)

The code we are using was copied nearly verbatim from the Preview example on the React Dropzon Web Site, so I'm wondering if I missed something here?

Here's what I've tried —

  1. Create a react-dropzone <Dropzone> page, with have it set to accept any file type, and giving it the ability to do Previews with the code from https://react-dropzone.js.org/#previews, having the "Preview" <aside> at the bottom of the <Dropzone> section of the code.
  2. Drag and Drop any sort of Image file (*.png, *.jpg or *.jpeg, *.gif, etc.).
  3. Look at the Preview that appears (should be ok, a thumbnail of the image file).
  4. Now Drag and Drop any other sort of file (.doc, .xls, *.pdf, etc.) onto the <Dropzone>.
  5. Look at the Preview that appears for that file (should be blank w/ a "broken file" image and whatever alt="..." text you have in there to describe the file).

Dropzone gets imported at the top of the file—

import React, { Component, /* useCallback */ } from "react";
...
import Dropzone from "react-dropzone";
...
Run Code Online (Sandbox Code Playgroud)

Styling/CSS Code for Thumbnails —

...
const thumbsContainer = {
  display: "flex",
  flexDirection: "row",
  flexWrap: "wrap",
  marginTop: 16
};

const thumb = {
  display: "inline-flex",
  borderRadius: 2,
  border: "1px solid #eaeaea",
  marginBottom: 8,
  marginRight: 8,
  width: 100,
  height: 100,
  padding: 4,
  boxSizing: "border-box"
};

const thumbInner = {
  display: "flex",
  minWidth: 0,
  overflow: "hidden"
};

const img = {
  display: "block",
  width: "auto",
  height: "100%"
};
...
Run Code Online (Sandbox Code Playgroud)

The onDrop() callback function—

...
onDrop = (acceptedFiles, rejectedFiles) => {
  let files = acceptedFiles.map(async file => {
    let data = new FormData();
    data.append("file", file);

    let item = await axios
      .post("form/upload", data, {
        headers: {
          "X-Requested-With": "XMLHttpRequest",
          "Content-Type": "application/x-www-form-urlencoded"
        }
      })
      .then(response => {
        return Object.assign(file, {
          preview: URL.createObjectURL(file),
          filename: response.data.filename
        });
      })
      .catch(err => {
        let rejects = rejectedFiles.map(async file => {
          let data = new FormData();
          await data.append("file", file);

          console.log("There was an error while attempting to add your files:", err);
          console.log("The files that were rejected were:\n", rejects.join('\n'));
        })
      });
    return item;
  });
  Promise.all(files)
    .then(completed => {
      let fileNames = completed.map(function(item) {
        return item["filename"];
      });
      this.setState({ files: completed, fileNames: fileNames });
    })
    .catch(err => {
      console.log('DROPZONE ERROR:', err);
    });
  };
...
Run Code Online (Sandbox Code Playgroud)

And the actual JSX code using <Dropzone> in the React.JS return()

...
<Form.Field>
  <label>Upload Files or Screenshots</label>
  <Dropzone accept={acceptedFileTypes} onDrop={this.onDrop}>
    {({ getRootProps, getInputProps, isDragActive }) => {
      return (
        <div
          {...getRootProps()}
          className={classNames("dropzone", {
            "dropzone--isActive": isDragActive
          })}
        >
          <input {...getInputProps()} />
          {isDragActive ? (
            <div>
              <div className="centered">
                <Icon name="cloud upload" size="big" />
              </div>
              <div className="centered">Drop Files Here.</div>
              <div className="centered">
                <Button className="drop-button">
                  Or Click to Select
                </Button>
              </div>
            </div>
          ) : (
            <div>
              <div className="centered">
                <Icon name="cloud upload" size="big" />
              </div>
              <div className="centered">
                Drag and Drop Supporting Files here to Upload.
              </div>
              <div className="centered">
                <Button className="drop-button">
                  Or Click to Select
                </Button>
              </div>
            </div>
          )}
        </div>
      );
    }}
  </Dropzone>
  <aside style={thumbsContainer}>{thumbs}</aside>
</Form.Field>
...
Run Code Online (Sandbox Code Playgroud)

Expected behavior —

I would prefer all file types to generate a correct preview.

My Set-up —

  • MacBook Pro 13-Inch 2018
  • 2.7 GHz Intel Core i7 processor
  • 16 GB 2133 MHz LPDDR3 RAM memory
  • MacOS version 10.14.4
  • React & React-DOM versions 16.5.2
  • Node.JS version 10.15.2
  • yarn version 1.15.2
  • React Dropzone version 10.1.4
  • Google Chrome for Mac OS Browser version 74.0.3729.131 (Official Build) (64-bit)
  • Upload to an AWS EC2 instance container server.

... So, am I doing something wrong so that previews on other files besides Images aren't working? Is this not a feature for anything but Image files? Please advise.

A couple other questions —

  1. If Previews are not able to be generated for files other than Image files with react-dropzone, is there a way to have Image Files generate Previews, but all other files just list the File name being uploaded and such, as in the some of the other examples on your React Dropzone Web Site? If so, how do you switch between the two to have both as you drag & drop files onto the <Dropzone>?
  2. In our code, which, again, we copied nearly verbatim from your examples at the React Dropzone Web Site, we have noticed that when you drag and drop on set of files onto the <Dropzone>, and then attempt to drag and drop a second time, that wipes the first set of files from being uploaded and replaces them with the new files just dropped on the <Dropzone>. Is there a way to have the <Dropzone> cumulatively add files on each time they are dropped there, not just wipe out the previous files and replace with the new? If so, what are the steps to do that, please?
  3. Is there a way, in the case of having the blank box File Preview for all other files, to get rid of the "Broken File" icon?

I do appreciate any constructive reply, please. Thank you in advance.

Scr*_*jet 0

只是为了澄清一下,React-dropzone 不是在页面上显示预览的那个。您正在使用图像标签来做到这一点。
现在,如果您要求显示所有文档文件类型的预览,您首先需要一个所需类型的解析器,因为浏览器不支持读取 pdf 以外的文档(.doc/.xlsx)。像react-doc-viewer
之类的东西。

这样您就可以读取上传的文件并使用包渲染器来显示文件

const docs = [
    { uri: "https://url-to-my-pdf.pdf" },
    { uri: require("./example-files/pdf.pdf") }, // Local File
];
... 
<DocViewer documents={docs} />;
Run Code Online (Sandbox Code Playgroud)
  1. 如果您不想安装库来执行此操作而只需要显示文件名,则只需更改显示所选文件的代码部分即可。
const thumbs = files.map(file => {
  // Add other images types as needed
  if(["image/png", "image/jpeg"].includes(file.type)){
    return (
      <div style={thumb} key={file.name}>
        <div style={thumbInner}>
          <img
            src={file.preview}
            style={img}
            // Revoke data uri after image is loaded
            onLoad={() => { URL.revokeObjectURL(file.preview) }}
          />
        </div>
      </div>
    );
  }
  // Handling other file types
  return (
    <div style={thumb} key={file.name}>
      <div style={thumbInner}>
        <p>{file.name}</p
      </div>
    </div>
  );
});
Run Code Online (Sandbox Code Playgroud)

您可以为不同的文件 MIME 类型添加不同的条件。文件 mime 类型的完整列表可以在mdn 文档
中找到

  1. 如果要将数据附加到同一列表,则需要更改 onDrop() 函数,因为在 Promise 解析部分中您将覆盖列表。
let fileNames = [...this.state.filenames, 
   ...completed.map(function(item) {
     return item["filename"];
})];
this.setState({files: [...this.state.files, ...completed], fileNames: fileNames });
Run Code Online (Sandbox Code Playgroud)
  1. 第 1 部分应该解决这个问题