前端环境准备及目录结构:
npm create vue 并取名为big-file-upload-fontend 通过 npm i 安装以下内容 "dependencies": { "axios": "^1.7.9", "element-plus": "^2.9.1", "js-sha256": "^0.11.0", "vue": "^3.5.13" },
main.js中的内容
import { createapp } from 'vue' import app from './app.vue' import elementplus from 'element-plus' import 'element-plus/dist/index.css' // app.use(elementplus) const app = createapp(app) app.use(elementplus) app.mount('#app')
app.vue 中的内容:
<template> <div class="button"> <el-upload ref="uploadref" class="upload-demo" :http-request="uploadfile" :show-file-list="false" > <el-button type="primary">点击上传文件</el-button> </el-upload> </div> </template> <script setup> import axios from 'axios' import {sha256} from 'js-sha256' const uploadfile = ({file}) => { // 每4mb为一个小文件 const chunksize = 4 * 1024 * 1024; // 4mb // 文件总大小 const filesize = file.size; // 分成了多少个片段 const chunks = math.ceil(filesize / chunksize); // 保证文件唯一 const sha256promise = sha256(file.name); // sha256的参数只接收字符串 // 询问已经上传了几个片段 const checkuploadedchunks = () => { return axios.post('http://127.0.0.1:8000/api/check', { sha256promise: sha256promise }).then(response => { return response.data; // response.data 就是下边的 uploadedchunks }); }; return checkuploadedchunks().then(async uploadedchunks => { if (uploadedchunks.length === chunks) { console.log("已经上传完成就不需要再重复上传") return promise.resolve(); } for (let i = 0; i < chunks; i++) { const formdata = new formdata(); // 将之前上传过的片段过滤掉,即不上传之前上传过的内容 if (uploadedchunks.includes(i + 1)) { continue; } const start = i * chunksize; // 将文件分片 const chunk = file.slice(start, start + chunksize); // 使用formdata形式上传文件 formdata.append('chunk', chunk); formdata.append('chunknumber', i + 1); formdata.append('chunkstotal', chunks); formdata.append('sha256promise', sha256promise); formdata.append('filename', file.name); // 一次只上传一个片段,本次上传完成后才上传下一个 const res = await axios.post('http://127.0.0.1:8000/api/upload', formdata) } }); }; </script> <style > html, body{ height: 100%; width: 100%; background-color: pink; } </style>
django后端环境及目录:
django-admin startproject big_file_upload_backend # 创建一个big_file_upload_backend项目
python版本:python 3.11.11
pip 需要安装:
django 5.0.6
django-cors-headers 4.6.0 # 用于解决跨域
big_file_upload_backend/settings.py 中的配置如下:
middleware = [ ...... 'django.contrib.sessions.middleware.sessionmiddleware', "corsheaders.middleware.corsmiddleware", # corsmiddleware一定要在commonmiddleware之前 'django.middleware.common.commonmiddleware', # 'django.middleware.csrf.csrfviewmiddleware', # 注释掉这个 'django.contrib.auth.middleware.authenticationmiddleware', ...... ] # 并在文件最后添加上允许所有跨域 cors_allow_credentials = true cors_allow_all_origins = true cors_allow_headers = ('*') # 将static_url = 'statics/' 替换为下边这三行 static_url = 'statics/' staticfiles_dirs = [ os.path.join(base_dir, "../statics"), ] # 完整的setting设置如下: """ django settings for big_file_upload_backend project. generated by 'django-admin startproject' using django 4.2. for more information on this file, see https://docs.djangoproject.com/en/4.2/topics/settings/ for the full list of settings and their values, see https://docs.djangoproject.com/en/4.2/ref/settings/ """ import os from pathlib import path # build paths inside the project like this: base_dir / 'subdir'. base_dir = path(__file__).resolve().parent.parent # quick-start development settings - unsuitable for production # see https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ # security warning: keep the secret key used in production secret! secret_key = 'django-insecure-%sa^&p^%m3+m0ex%@y%la0(zzt4y4k3l%0=p#tipx-kz6w*#=d' # security warning: don't run with debug turned on in production! debug = true allowed_hosts = [] # application definition installed_apps = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] middleware = [ 'django.middleware.security.securitymiddleware', 'django.contrib.sessions.middleware.sessionmiddleware', "corsheaders.middleware.corsmiddleware", 'django.middleware.common.commonmiddleware', # 'django.middleware.csrf.csrfviewmiddleware', 'django.contrib.auth.middleware.authenticationmiddleware', 'django.contrib.messages.middleware.messagemiddleware', 'django.middleware.clickjacking.xframeoptionsmiddleware', ] root_urlconf = 'big_file_upload_backend.urls' templates = [ { 'backend': 'django.template.backends.django.djangotemplates', 'dirs': [], 'app_dirs': true, 'options': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] wsgi_application = 'big_file_upload_backend.wsgi.application' # database # https://docs.djangoproject.com/en/4.2/ref/settings/#databases databases = { 'default': { 'engine': 'django.db.backends.sqlite3', 'name': base_dir / 'db.sqlite3', } } # password validation # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators auth_password_validators = [ { 'name': 'django.contrib.auth.password_validation.userattributesimilarityvalidator', }, { 'name': 'django.contrib.auth.password_validation.minimumlengthvalidator', }, { 'name': 'django.contrib.auth.password_validation.commonpasswordvalidator', }, { 'name': 'django.contrib.auth.password_validation.numericpasswordvalidator', }, ] # internationalization # https://docs.djangoproject.com/en/4.2/topics/i18n/ language_code = 'en-us' time_zone = 'utc' use_i18n = true use_tz = true # static files (css, javascript, images) # https://docs.djangoproject.com/en/4.2/howto/static-files/ static_url = 'statics/' staticfiles_dirs = [ os.path.join(base_dir, "../statics"), ] # default primary key field type # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field default_auto_field = 'django.db.models.bigautofield' cors_allow_credentials = true cors_allow_all_origins = true cors_allow_headers = ('*')
big_file_upload_backend/urls.py中的配置如下:
from django.contrib import admin from django.urls import path from big_file_upload_backend.views import checks, upload urlpatterns = [ path('admin/', admin.site.urls), path('api/check', checks), path('api/upload', upload), ]
big_file_upload_backend/views.py中的配置如下:
import json import os from django.http import jsonresponse from big_file_upload_backend import settings def checks(request): if request.method == "post": body = json.loads(request.body.decode("utf-8")) filename = body.get("sha256promise", none) base_path = settings.static_url+'record_files/'+filename+'.txt' # base_path = '../statics/record_files/'+filename+'.txt' file_is_exits = os.path.exists(base_path) # 判断文件是否存在,存在则说明之前至少上传过一次 if file_is_exits: with open(base_path, 'r') as f: check_list = json.loads(f.readline()) else: # 不存在就返回空 check_list = [] return jsonresponse(check_list, safe=false) def upload(request): if request.method == "post": # 注意这里用的是request.files 因为chunk为文件流形式 chunk = request.files.get("chunk") # 当前的切片编号 chunk_number = request.post.get("chunknumber") # 总切片数量 chunks_total = int(request.post.get("chunkstotal")) # 文件名唯一标识 sha256_promise = request.post.get("sha256promise") # 文件名称 filename = request.post.get("filename") # base_path = '../statics/upload_files/'+sha256_promise # 这样写无效 base_path = settings.static_url + "upload_files/" + sha256_promise # 必须这样写 # record_path中的txt文件用于记录已经上传过的切片 record_path = settings.static_url + "record_files/" + sha256_promise+'.txt' # 必须这样写 os.makedirs(base_path, exist_ok=true) # 后缀名 ext = filename.split(".")[-1] # 小切片的文件名称 order_file = chunk_number+'.'+ext fname = base_path+"/" + order_file with open(fname, 'wb') as f: for line in chunk: # 将上传的文件写入小切片中,等上传完成后进行合并 f.write(line) # 等写完了才做判断 chunk_number_int = int(chunk_number) # 将字符串转成int line_list = [int(chunk_number)] # 默认先添加一个切片片段 if os.path.exists(record_path): with open(record_path, 'r') as f: line_data = f.readline() # 读取已经上传的小切片 if line_data == '': pass else: line_list = json.loads(line_data) # 将字符串形式的数组转为python数组 if chunk_number_int not in line_list: line_list.append(chunk_number_int) # 如果当前切片号不在已上传的数组中则添加 with open(record_path, 'w') as f: f.write(json.dumps(line_list)) # 将已上传的片段列表重新写回记录文件中 # 合并小切片片段段 if len(line_list) == chunks_total: with open(base_path+"/"+filename, "wb") as f: # 按照升序一个一个合并 for num in sorted(line_list): with open(base_path+"/"+str(num)+"."+ext, 'rb') as r: f.write(r.read()) # 读取完毕将片段删除,只保留合并后的文件 os.remove(base_path+"/"+str(num)+"."+ext) # 返回没啥用 check_list = {"chunk_number": chunk_number, "code": 200} return jsonresponse(check_list)
在项目根目录下要新建一个statics目录,且其下边要有两个目录:
record_files upload_files
最后分别运行前后端项目
前端:npm run dev 后端: python manage.py runserver
点击上传文件,选择一个较大的文件进行上传,可以看到右侧一直再分片上传,上传完成后会在上述两个文件中分别多出两个文件
如果是上传过程中还可以看到upload_files文件下的小分片中的片段产生和合并过程,也可以在上传到一半时随机关闭浏览器,下次打开重新上传,则会跳过之前上传的继续进行上传。上传完成后的效果:
到此这篇关于django vue3实现大文件分段续传(断点续传)的文章就介绍到这了,更多相关django 大文件分段续传内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论