酷奇 發表於 2023-1-29 15:48:00

JavaScript 文件上传与下载

<p><span style="font-size: 16px"><strong>一、文件上传</strong></span></p>
<p><span style="font-size: 15px"><strong>1、普通文件上传</strong></span></p>
<p>JavaScript 可以使用表单提交来实现文件上传。首先,在 HTML 中创建一个文件输入框:</p>
<pre class="language-html highlighter-hljs"><code>&lt;input type="file" id="fileInput"&gt;</code></pre>
<p>然后,在 JavaScript 中获取文件输入框的引用,并在其上设置事件监听器,如下所示:</p>
<pre class="language-javascript highlighter-hljs"><code>var fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', function () {
// 在这里处理选择的文件
});</code></pre>
<p>在事件监听器中,可以使用 <code>fileInput.files</code> 属性获取选择的文件,然后对文件进行处理。</p>
<p>接下来可以使用 <code>XMLHttpRequest</code> 或 <code>fetch</code> API 来上传文件。</p>
<p>使用 <code>XMLHttpRequest</code>:</p>
<pre class="language-javascript highlighter-hljs"><code>var file = fileInput.files;
var formData = new FormData();
formData.append('file', file);

var xhr = new XMLHttpRequest();
xhr.open('POST', 'url', true);
xhr.onload = function () {
if (xhr.status === 200) {
    console.log('upload success');
}
};
xhr.send(formData);</code></pre>
<p>使用 <code>fetch</code>:</p>
<pre class="language-javascript highlighter-hljs"><code>var file = fileInput.files;
var formData = new FormData();
formData.append('file', file);

fetch('url', {
method: 'POST',
body: formData
}).then(response =&gt; {
if (response.ok) {
    console.log('upload success');
}
});</code></pre>
<div class="w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-50 dark:bg-[#444654]">
<div class="text-base gap-4 md:gap-6 m-auto md:max-w-2xl lg:max-w-2xl xl:max-w-3xl p-4 md:py-6 flex lg:px-0">
<div class="relative flex w- flex-col gap-1 md:gap-3 lg:w-">
<div class="flex flex-grow flex-col gap-3">
<div class="min-h- flex flex-col items-start gap-4 whitespace-pre-wrap">
<div class="markdown prose w-full break-words dark:prose-invert light">
<p>另外还可以使用第三方库如 <code>axios</code> 来实现文件上传,具体实现方法可以参考相关文档。</p>
<p><span style="font-size: 15px"><strong>2、大文件上传</strong></span></p>
<p><strong>2.1、前端实现代码</strong></p>
<p>在上传大文件时,通常采用<strong>分块上传</strong>的方式。将大文件分成若干个块,每块一个 HTTP 请求上传。</p>
<p>实现大文件上传的步骤如下:</p>
<ol>
<li>用户选择文件。</li>
<li>将文件分成若干块。</li>
<li>对于每一块,向服务器发送 HTTP 请求上传。</li>
<li>服务器接收到文件块后,将其存储在服务器上。</li>
<li>在所有块上传完成后,服务器将所有块合并成一个完整的文件。</li>
</ol>
<p>JavaScript 可以使用 File API(File 和 Blob 对象)来实现文件的读取和上传。</p>
<p>下面是一个使用 JavaScript 实现大文件上传的简单实例,使用分块上传的方法。</p>
<pre class="language-html highlighter-hljs"><code>&lt;input type="file" id="file-input"&gt;</code></pre>
<p>&nbsp;</p>
<pre class="language-javascript highlighter-hljs"><code>// 上传文件块
function uploadChunk(file, start, end, chunk) {
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/upload', true);
    xhr.setRequestHeader('Content-Type', 'application/octet-stream');
    xhr.setRequestHeader('Content-Range', 'bytes ' + start + '-' + end + '/' + file.size);
    xhr.send(chunk);
}

// 上传文件
function uploadFile(file) {
    var chunkSize = 1 * 1024 * 1024; // 分块大小为1MB
    var chunks = Math.ceil(file.size / chunkSize); // 计算分块数
    var currentChunk = 0; // 当前分块
    var start, end;
    while (currentChunk &lt; chunks) {
      start = currentChunk * chunkSize;
      end = start + chunkSize &gt;= file.size ? file.size : start + chunkSize;
      var chunk = file.slice(start, end);
      uploadChunk(file, start, end, chunk);
      currentChunk++;
    }
}

// 监听文件选择事件
document.getElementById('file-input').addEventListener('change', function(e) {
    var file = e.target.files;
    if (file) {
      uploadFile(file);
    }
});</code></pre>
<p>在这个实例中,我们使用了 XMLHttpRequest 对象上传文件,并设置了 Content-Type 和 Content-Range 消息头。Content-Type 消息头表示上传的数据类型是二进制数据,Content-Range 消息头表示上传的文件块的范围。</p>
<p>在这个示例中,我们将文件分成若干块,每块大小为 1MB。我们使用 File API 中的 slice 方法截取文件块,并使用 XMLHttpRequest 将文件块上传到服务器。</p>
<p>注意,这只是一个简单的实例,代码仅供参考,在实际应用中还需要考在考虑以下几点:</p>
<ul>
<li>如果服务器端支持断点续传,可以在服务器端记录已经上传的文件块,避免重复上传。</li>
<li>需要考虑如何处理上传失败的文件块,是否需要重试。</li>
<li>在上传过程中需要提供进度条,让用户了解上传进度。</li>
<li>在上传完成后需要有反馈,告知用户上传是否成功。</li>
<li>服务器端如何处理上传的文件块,将其合并成一个完整的文件。</li>
<li>服务器端存储空间的问题。可以使用分布式文件系统(如 HDFS)或云存储(如 Amazon S3)来存储上传的文件。</li>
<li>文件块上传顺序、文件块校验、断点续传等问题。</li>
</ul>
<p>通过分块上传的方式,我们可以将大文件分成若干块上传,避免一次性上传大文件造成的超时或者内存不足的问题,同时也方便实现断点续传和上传进度的显示。</p>
<p>除了上面提到的方法外,还可以使用第三方库来实现大文件上传。常见的第三方库有:</p>
<ul>
<li>resumable.js</li>
<li>plupload</li>
<li>fine-uploader</li>
<li>tus-js-client</li>
</ul>
<p>这些库都提供了文件分块、断点续传、上传进度等功能,可以让你更快捷地实现大文件上传。</p>
<p>不过要注意的是,使用第三方库可能会增加代码的复杂性和对第三方库的依赖。在选择使用第三方库时需要权衡其优缺点,并确保它满足你的需求。</p>
<p><strong>2.2、后端实现代码</strong></p>
<p>Java 后端代码示例:</p>
<pre class="language-java highlighter-hljs"><code>import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;

public class FileUploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    // 保存所有分块数据,使用ConcurrentHashMap保证线程安全
    private Map&lt;String, byte[]&gt; chunks = new ConcurrentHashMap&lt;&gt;();

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      if (!ServletFileUpload.isMultipartContent(request)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not a multipart request");
            return;
      }

      ServletFileUpload upload = new ServletFileUpload();
      try {
            FileItemIterator iter = upload.getItemIterator(request);
            while (iter.hasNext()) {
                FileItemStream item = iter.next();
                if (!item.isFormField()) {
                  // 处理文件分块
                  processFilePart(item);
                }
            }
      } catch (Exception e) {
            e.printStackTrace();
      }
    }

    private void processFilePart(FileItemStream item) throws Exception {
      String index = item.getFieldName();
      byte[] data = IOUtils.toByteArray(item.openStream());
      chunks.put(index, data);

      // 如果所有分块都已经上传完成,合并所有分块并保存文件
      if (isAllChunksUploaded()) {
            // 合并所有分块并保存文件
            mergeAndSaveFile("/path/filename.suffix");
      }
    }

    private boolean isAllChunksUploaded() {
      // 判断是否所有分块都已经上传完成
      int totalChunks = getTotalChunks();
      for (int i = 0; i &lt; totalChunks; i++) {
            if (!chunks.containsKey(String.valueOf(i))) {
                return false;
            }
      }
      return true;
    }
   
    public void mergeAndSaveFile(String fileName) throws Exception {
      int totalChunks = getTotalChunks();
      int totalFileSize = getTotalFileSize();
      byte[] mergedFile = new byte;
      int index = 0;
      for (int i = 0; i &lt; totalChunks; i++) {
            byte[] chunkData = chunks.get(String.valueOf(i));
            System.arraycopy(chunkData, 0, mergedFile, index, chunkData.length);
            index += chunkData.length;
      }
      saveFileToLocal(mergedFile, fileName);
    }
   
    private void saveFileToLocal(byte[] fileData, String filePath) throws Exception {
      try (FileOutputStream fos = new FileOutputStream(filePath)) {
            fos.write(fileData);
      }
    }

    private int getTotalChunks() {
      // You need to implement this method
      return 0;
    }

    private int getTotalFileSize() {
      // You need to implement this method
      return 0;
    }
}</code></pre>
<p>这只是一个示例代码,不是完整的项目。该代码使用了 Apache Commons FileUpload 库,在运行该代码之前请确保已经引入了该库。该代码实现了分块上传的主要逻辑,包括存储分块数据,合并所有分块并保存文件,以及判断是否所有分块都已经上传。</p>
<p>请注意,上面的代码中的 <code>getTotalChunks</code> 和 <code>getTotalFileSize</code> 方法需要您自行实现。它们将分别用于获取总分块数和总文件大小,这些信息可以通过前端预先发送给后端或从数据库中查询获得。</p>
<p>&nbsp;</p>
<p>关于文件上传需要了解的知识点:</p>
<ul>
<li>HTML5 文件上传</li>
<li>文件上传技术</li>
<li>文件上传安全性</li>
<li>分块上传</li>
<li>断点续传</li>
<li>云存储</li>
</ul>
<p><span style="font-size: 16px"><strong>二、文件下载</strong></span></p>
<p><span style="font-size: 14px">在 JavaScript 中实现文件下载,常见的方法如下:</span></p>
<p><span style="font-size: 14px">1、使用 window.location 实现:通过更改当前页面的 URL 为文件下载地址,从而实现下载。</span></p>
<pre class="language-javascript highlighter-hljs"><code>window.location = 'file-download-url';</code></pre>
<p>2、使用 a 标签实现:通过创建一个 a 标签并设置其 href 和 download 属性,从而实现下载。</p>
<pre class="language-javascript highlighter-hljs"><code>var link = document.createElement("a");
link.download = "filename";
link.href = "file-download-url";
link.click();</code></pre>
<p>3、使用 fetch API 实现:通过使用 fetch API 获取文件内容,并将其写入 Blob 对象,最后利用 URL.createObjectURL 将其下载。</p>
<pre class="language-javascript highlighter-hljs"><code>fetch('file-download-url')
.then(response =&gt; response.blob())
.then(blob =&gt; {
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'filename';
    link.click();
    URL.revokeObjectURL(url);
    document.body.removeChild(link);
});</code></pre>
<p>&nbsp;</p>
</div>
</div>
</div>
</div>
</div>
</div><br><br>
来源:https://www.cnblogs.com/yuzhihui/p/17072894.html
頁: [1]
查看完整版本: JavaScript 文件上传与下载