Optimize file upload
This commit is contained in:
parent
b37aa3da6c
commit
cef7dc177e
|
@ -2,74 +2,65 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>XMLHttpRequest上传文件</title>
|
||||
<title>File upload</title>
|
||||
<script type="text/javascript">
|
||||
//图片上传
|
||||
var xhr;
|
||||
//上传文件方法
|
||||
//File uploading method
|
||||
function UpladFile() {
|
||||
var fileObj = document.getElementById("file").files[0]; // js 获取文件对象
|
||||
var url = "http://localhost:8080" + "/api/attachment/upload"; // 接收上传文件的后台地址
|
||||
var fileObj = document.getElementById("file").files[0]; // js get file object
|
||||
var url = "http://localhost:8848" + "/api/attachment/upload";
|
||||
|
||||
var form = new FormData(); // FormData 对象
|
||||
form.append("file", fileObj); // 文件对象
|
||||
var form = new FormData(); // FormData object
|
||||
form.append("file", fileObj); // File object
|
||||
|
||||
xhr = new XMLHttpRequest(); // XMLHttpRequest 对象
|
||||
xhr.open("post", url, true); //post方式,url为服务器请求地址,true 该参数规定请求是否异步处理。
|
||||
xhr.onload = uploadComplete; //请求完成
|
||||
xhr.onerror = uploadFailed; //请求失败
|
||||
xhr = new XMLHttpRequest(); // XMLHttpRequest object
|
||||
xhr.open("post", url, true); //post
|
||||
xhr.onload = uploadComplete;
|
||||
xhr.onerror = uploadFailed;
|
||||
|
||||
xhr.upload.onprogress = progressFunction;//【上传进度调用方法实现】
|
||||
xhr.upload.onloadstart = function(){//上传开始执行方法
|
||||
ot = new Date().getTime(); //设置上传开始时间
|
||||
oloaded = 0;//设置上传开始时,以上传的文件大小为0
|
||||
xhr.upload.onprogress = progressFunction;
|
||||
xhr.upload.onloadstart = function(){
|
||||
ot = new Date().getTime();
|
||||
oloaded = 0;
|
||||
};
|
||||
|
||||
xhr.send(form); //开始上传,发送form数据
|
||||
xhr.send(form);
|
||||
}
|
||||
|
||||
//上传成功响应
|
||||
function uploadComplete(evt) {
|
||||
//服务断接收完文件返回的结果
|
||||
|
||||
var data = JSON.parse(evt.target.responseText);
|
||||
if(data.success) {
|
||||
alert("上传成功!");
|
||||
if(data.result == "ok") {
|
||||
alert("Uploaded successfully!");
|
||||
}else{
|
||||
alert("上传失败!");
|
||||
alert("Upload failed!");
|
||||
}
|
||||
|
||||
}
|
||||
//上传失败
|
||||
|
||||
function uploadFailed(evt) {
|
||||
alert("上传失败!");
|
||||
alert("Upload failed!");
|
||||
}
|
||||
//取消上传
|
||||
|
||||
function cancleUploadFile(){
|
||||
xhr.abort();
|
||||
}
|
||||
|
||||
|
||||
//上传进度实现方法,上传过程中会频繁调用该方法
|
||||
function progressFunction(evt) {
|
||||
var progressBar = document.getElementById("progressBar");
|
||||
var percentageDiv = document.getElementById("percentage");
|
||||
// event.total是需要传输的总字节,event.loaded是已经传输的字节。如果event.lengthComputable不为真,则event.total等于0
|
||||
if (evt.lengthComputable) {//
|
||||
progressBar.max = evt.total;
|
||||
progressBar.value = evt.loaded;
|
||||
percentageDiv.innerHTML = Math.round(evt.loaded / evt.total * 100) + "%";
|
||||
}
|
||||
var time = document.getElementById("time");
|
||||
var nt = new Date().getTime();//获取当前时间
|
||||
var pertime = (nt-ot)/1000; //计算出上次调用该方法时到现在的时间差,单位为s
|
||||
ot = new Date().getTime(); //重新赋值时间,用于下次计算
|
||||
var perload = evt.loaded - oloaded; //计算该分段上传的文件大小,单位b
|
||||
oloaded = evt.loaded;//重新赋值已上传文件大小,用以下次计算
|
||||
//上传速度计算
|
||||
var speed = perload/pertime;//单位b/s
|
||||
var nt = new Date().getTime();
|
||||
var pertime = (nt-ot)/1000;
|
||||
ot = new Date().getTime();
|
||||
var perload = evt.loaded - oloaded;
|
||||
oloaded = evt.loaded;
|
||||
var speed = perload/pertime;
|
||||
var bspeed = speed;
|
||||
var units = 'b/s';//单位名称
|
||||
var units = 'b/s';
|
||||
if(speed/1024>1){
|
||||
speed = speed/1024;
|
||||
units = 'k/s';
|
||||
|
@ -79,10 +70,9 @@
|
|||
units = 'M/s';
|
||||
}
|
||||
speed = speed.toFixed(1);
|
||||
//剩余时间
|
||||
var resttime = ((evt.total-evt.loaded)/bspeed).toFixed(1);
|
||||
time.innerHTML = ',速度:'+speed+units+',剩余时间:'+resttime+'s';
|
||||
if(bspeed==0) time.innerHTML = '上传已取消';
|
||||
time.innerHTML = ',Speed: '+speed+units+', the remaining time: '+resttime+'s';
|
||||
if(bspeed==0) time.innerHTML = 'Upload cancelled';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -91,7 +81,7 @@
|
|||
<span id="percentage"></span><span id="time"></span>
|
||||
<br /><br />
|
||||
<input type="file" id="file" name="myfile" />
|
||||
<input type="button" onclick="UpladFile()" value="上传" />
|
||||
<input type="button" onclick="cancleUploadFile()" value="取消" />
|
||||
<input type="button" onclick="UpladFile()" value="Upload" />
|
||||
<input type="button" onclick="cancleUploadFile()" value="Cancel" />
|
||||
</body>
|
||||
</html>
|
|
@ -11,27 +11,35 @@ void Attachment::get(const HttpRequestPtr &req,
|
|||
void Attachment::upload(const HttpRequestPtr &req,
|
||||
const std::function<void(const HttpResponsePtr &)> &callback)
|
||||
{
|
||||
FileUpload fileUpload;
|
||||
fileUpload.parse(req);
|
||||
auto files = fileUpload.getFiles();
|
||||
for (auto const &file : files)
|
||||
MultiPartParser fileUpload;
|
||||
if(fileUpload.parse(req)==0)
|
||||
{
|
||||
LOG_DEBUG << "file:"
|
||||
<< file.getFileName()
|
||||
<< "(len="
|
||||
<< file.fileLength()
|
||||
<< ",md5="
|
||||
<< file.getMd5()
|
||||
<< ")";
|
||||
file.save();
|
||||
file.save("123");
|
||||
file.saveAs("456/hehe");
|
||||
file.saveAs("456/7/8/9/"+file.getMd5());
|
||||
file.save("..");
|
||||
file.save(".xx");
|
||||
file.saveAs("../xxx");
|
||||
auto files = fileUpload.getFiles();
|
||||
for (auto const &file : files)
|
||||
{
|
||||
LOG_DEBUG << "file:"
|
||||
<< file.getFileName()
|
||||
<< "(len="
|
||||
<< file.fileLength()
|
||||
<< ",md5="
|
||||
<< file.getMd5()
|
||||
<< ")";
|
||||
file.save();
|
||||
file.save("123");
|
||||
file.saveAs("456/hehe");
|
||||
file.saveAs("456/7/8/9/" + file.getMd5());
|
||||
file.save("..");
|
||||
file.save(".xx");
|
||||
file.saveAs("../xxx");
|
||||
}
|
||||
Json::Value json;
|
||||
json["result"] = "ok";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(json);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k200OK);
|
||||
Json::Value json;
|
||||
json["result"] = "failed";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(json);
|
||||
callback(resp);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include <drogon/version.h>
|
||||
#include <drogon/NotFound.h>
|
||||
#include <drogon/HttpClient.h>
|
||||
#include <drogon/FileUpload.h>
|
||||
#include <drogon/MultiPart.h>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <drogon/CacheMap.h>
|
||||
#include <memory>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* FileUpload.h
|
||||
* MultiPart.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
|
@ -14,12 +14,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "HttpRequest.h"
|
||||
#include <drogon/HttpRequest.h>
|
||||
#include <drogon/HttpResponse.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
|
||||
class HttpFile
|
||||
{
|
||||
public:
|
||||
|
@ -31,6 +34,7 @@ class HttpFile
|
|||
|
||||
/// Set the contents of the file, usually called by the FileUpload parser.
|
||||
void setFile(const std::string &file) { _fileContent = file; };
|
||||
void setFile(std::string &&file) { _fileContent = std::move(file); }
|
||||
|
||||
/// Save the file to the file system.
|
||||
/**
|
||||
|
@ -66,11 +70,12 @@ class HttpFile
|
|||
std::string _fileName;
|
||||
std::string _fileContent;
|
||||
};
|
||||
class FileUpload
|
||||
|
||||
class MultiPartParser
|
||||
{
|
||||
public:
|
||||
FileUpload(){};
|
||||
~FileUpload(){};
|
||||
MultiPartParser(){};
|
||||
~MultiPartParser(){};
|
||||
/// Get files, This method should be called after calling the parse() method.
|
||||
const std::vector<HttpFile> &getFiles();
|
||||
|
||||
|
@ -80,9 +85,16 @@ class FileUpload
|
|||
/// Parse the http request stream to get files and parameters.
|
||||
int parse(const HttpRequestPtr &req);
|
||||
|
||||
/// Parse the http response stream to get files and parameters.
|
||||
int parse(const HttpResponsePtr &req);
|
||||
|
||||
protected:
|
||||
std::vector<HttpFile> _files;
|
||||
std::map<std::string, std::string> _parameters;
|
||||
int parseEntity(const std::string &str);
|
||||
int parse(const std::string &content, const std::string &boundary);
|
||||
int parseEntity(const char *begin, const char *end);
|
||||
};
|
||||
|
||||
typedef MultiPartParser FileUpload; /// In order to be compatible with old interfaces
|
||||
|
||||
} // namespace drogon
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* FileUpload.cc
|
||||
* MultiPart.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
|
@ -15,7 +15,7 @@
|
|||
#include "HttpRequestImpl.h"
|
||||
#include "HttpUtils.h"
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <drogon/FileUpload.h>
|
||||
#include <drogon/MultiPart.h>
|
||||
#include <drogon/HttpAppFramework.h>
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/md5.h>
|
||||
|
@ -31,15 +31,17 @@
|
|||
|
||||
using namespace drogon;
|
||||
|
||||
const std::vector<HttpFile> &FileUpload::getFiles()
|
||||
const std::vector<HttpFile> &MultiPartParser::getFiles()
|
||||
{
|
||||
return _files;
|
||||
}
|
||||
const std::map<std::string, std::string> &FileUpload::getParameters() const
|
||||
|
||||
const std::map<std::string, std::string> &MultiPartParser::getParameters() const
|
||||
{
|
||||
return _parameters;
|
||||
};
|
||||
int FileUpload::parse(const HttpRequestPtr &req)
|
||||
|
||||
int MultiPartParser::parse(const HttpRequestPtr &req)
|
||||
{
|
||||
if (req->method() != Post)
|
||||
return -1;
|
||||
|
@ -61,7 +63,59 @@ int FileUpload::parse(const HttpRequestPtr &req)
|
|||
return -1;
|
||||
std::string boundary = contentType.substr(pos + 9);
|
||||
//std::cout << "boundary[" << boundary << "]" << std::endl;
|
||||
std::string content = req->query();
|
||||
auto &content = req->query();
|
||||
return parse(content, boundary);
|
||||
}
|
||||
|
||||
int MultiPartParser::parse(const HttpResponsePtr &resp)
|
||||
{
|
||||
/// TODO:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MultiPartParser::parseEntity(const char *begin, const char *end)
|
||||
{
|
||||
static const char entityName[] = "name=\"";
|
||||
static const char quotationMark[] = "\"";
|
||||
static const char fileName[] = "filename=\"";
|
||||
static const char CRLF[] = "\r\n\r\n";
|
||||
|
||||
auto pos = std::search(begin, end, entityName, entityName + 6);
|
||||
if (pos == end)
|
||||
return -1;
|
||||
pos += 6;
|
||||
auto pos1 = std::search(pos, end, quotationMark, quotationMark + 1);
|
||||
if (pos1 == end)
|
||||
return -1;
|
||||
std::string name(pos, pos1);
|
||||
pos = std::search(pos1, end, fileName, fileName + 10);
|
||||
if (pos == end)
|
||||
{
|
||||
pos1 = std::search(pos1, end, CRLF, CRLF + 4);
|
||||
if (pos1 == end)
|
||||
return -1;
|
||||
_parameters[name] = std::string(pos1 + 4, end);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos += 10;
|
||||
auto pos1 = std::search(pos, end, quotationMark, quotationMark + 1);
|
||||
if (pos1 == end)
|
||||
return -1;
|
||||
HttpFile file;
|
||||
file.setFileName(std::string(pos, pos1));
|
||||
pos1 = std::search(pos1, end, CRLF, CRLF + 4);
|
||||
if (pos1 == end)
|
||||
return -1;
|
||||
file.setFile(std::string(pos1 + 4, end));
|
||||
_files.push_back(std::move(file));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int MultiPartParser::parse(const std::string &content, const std::string &boundary)
|
||||
{
|
||||
std::string::size_type pos1, pos2;
|
||||
pos1 = 0;
|
||||
pos2 = content.find(boundary);
|
||||
|
@ -79,59 +133,12 @@ int FileUpload::parse(const HttpRequestPtr &req)
|
|||
// std::cout<<"pos1="<<pos1<<" pos2="<<pos2<<std::endl;
|
||||
if (content[pos2 - 4] == '\r' && content[pos2 - 3] == '\n' && content[pos2 - 2] == '-' && content[pos2 - 1] == '-')
|
||||
pos2 -= 4;
|
||||
if (parseEntity(content.substr(pos1, pos2 - pos1)) != 0)
|
||||
if (parseEntity(content.c_str() + pos1, content.c_str() + pos2) != 0)
|
||||
return -1;
|
||||
//pos2+=boundary.length();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int FileUpload::parseEntity(const std::string &str)
|
||||
{
|
||||
// parseEntity:[Content-Disposition: form-data; name="userfile1"; filename="appID.txt"
|
||||
// Content-Type: text/plain
|
||||
//
|
||||
// AA004MV7QI]
|
||||
// pos1=190 pos2=251
|
||||
// parseEntity:[YvBu
|
||||
// Content-Disposition: form-data; name="text"
|
||||
//
|
||||
// text]
|
||||
|
||||
// std::cout<<"parseEntity:["<<str<<"]"<<std::endl;
|
||||
std::string::size_type pos = str.find("name=\"");
|
||||
if (pos == std::string::npos)
|
||||
return -1;
|
||||
pos += 6;
|
||||
std::string::size_type pos1 = str.find("\"", pos);
|
||||
if (pos1 == std::string::npos)
|
||||
return -1;
|
||||
std::string name = str.substr(pos, pos1 - pos);
|
||||
// std::cout<<"name=["<<name<<"]\n";
|
||||
pos = str.find("filename=\"");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
pos1 = str.find("\r\n\r\n");
|
||||
if (pos1 == std::string::npos)
|
||||
return -1;
|
||||
_parameters[name] = str.substr(pos1 + 4);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos += 10;
|
||||
std::string::size_type pos1 = str.find("\"", pos);
|
||||
if (pos1 == std::string::npos)
|
||||
return -1;
|
||||
HttpFile file;
|
||||
file.setFileName(str.substr(pos, pos1 - pos));
|
||||
pos1 = str.find("\r\n\r\n");
|
||||
if (pos1 == std::string::npos)
|
||||
return -1;
|
||||
file.setFile(str.substr(pos1 + 4));
|
||||
_files.push_back(file);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int HttpFile::save(const std::string &path) const
|
||||
{
|
||||
|
@ -145,7 +152,7 @@ int HttpFile::save(const std::string &path) const
|
|||
(path.length() >= 3 && path[0] == '.' && path[1] == '.' && path[2] == '/') ||
|
||||
path == "." || path == "..")
|
||||
{
|
||||
//Absolute or relative path
|
||||
//Absolute or relative path
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -180,7 +187,7 @@ int HttpFile::saveAs(const std::string &filename) const
|
|||
(filename.length() >= 2 && filename[0] == '.' && filename[1] == '/') ||
|
||||
(filename.length() >= 3 && filename[0] == '.' && filename[1] == '.' && filename[2] == '/'))
|
||||
{
|
||||
//Absolute or relative path
|
||||
//Absolute or relative path
|
||||
}
|
||||
else
|
||||
{
|
Loading…
Reference in New Issue