前言
在很多时候,我们开发http后端服务器都需要在前端和后端之间传输图片,本文将介绍http服务器传输图片时的技术要点。
一、浏览器上传图片
一般的浏览器上传图片均是使用multipart/form-data的Content-Type进行图片的传输,当然这种类型不仅仅是用于传图片,也用于传输任意文件。html网页使用的代码如下:
<form method="POST" action="http://你的网站的接口" enctype="multipart/form-data">
<input type="file" name="file" size="31">
<input type="submit" value="Upload">
</form>
http服务器收到的数据可以使用wireshark来进行抓包分析,收到的request如下:
http服务器处理的要点时,找到你使用的语言构建的服务器是如何处理HTMLForm 的,HTMLForm 不同于JSON或者url附带的参数,需要进行特别处理。HTMLForm里面有Content-Disposition: form-data; name=“file”; filename=“2.jpg” 用来描述上传的文件的名字。还有一个Content-Type: image/jpeg 属性用来描述文件的类型。HTMLForm使用浏览器指定的边界符号Boundary: "----WebKitFormBoundary85RVxRKo9Q6jwEKL"来确认文件的边界。不同的框架对HTMLForm的处理不一致。
比如我用的是Poco C++框架,处理过程如下:
response.setChunkedTransferEncoding(true);
response.setContentType("application/json");
std::ostream &ostr = response.send();
try
{
MyPartHandler partHandler;
HTMLForm form(request, request.stream(), partHandler);
std::ofstream outfile;
outfile.open("Image/1.jpg", std::ios::binary | std::ios::out | std::ios::trunc);
outfile.write(&partHandler.buffer()[0],partHandler.length());
outfile.close();
returnMsg.set("msg"," "+partHandler.fileName()+" have been received successfully");
std::string temp= Poco::Dynamic::Var(returnMsg).toString();
ostr<<temp;
}
catch(const Poco::Exception& e)
{
app.logger().log(e,__FILE__,__LINE__);
}
//自己的处理类,用来获取HTMLForm里面的信息
class MyPartHandler: public Poco::Net::PartHandler
{
public:
MyPartHandler():
_length(0)
{
}
void handlePart(const MessageHeader& header, std::istream& stream)
{
_type = header.get("Content-Type", "(unspecified)");
if (header.has("Content-Disposition"))
{
std::string disp;
NameValueCollection params;
MessageHeader::splitParameters(header["Content-Disposition"], disp, params);
_name = params.get("name", "(unnamed)");//获取网页给文件对应的key名
_fileName = params.get("filename", "(unnamed)");//获取上传的文件名
}
StreamCopier::copyToString(stream, _buffer);//二进制数据写入到buffer
_length=_buffer.size(); //获取二进制数据长度
}
}
实际上,二进制的数据永远只是数据,具体还要看你把这些数据保存为什么文件,服务器后端收到的就是什么格式的文件。
二、浏览器下载图片
1.后端
后端对应传输图片给前端,只要注意两件事:
第一,response的ContentType一定是image/jpeg或者image下面的其它文件类型。
第二,返回的文件数据一定要完整,这个可以通过对比浏览器收到的数据的Content-Length和文件本身的大小来判断,是否是后端没有传输完文件。
Poco代码如下:
response.setContentType("image/jpeg");
std::ifstream infile;
infile.open("Image/1.jpg", std::ios::binary | std::ios::in | std::ios::ate);
long int length;
length = infile.tellg(); //读取文件长度
infile.seekg(0, std::ios::beg); //返回文件首部
char *buffer = new char[length];
infile.read(buffer, length); // 读取文件二进制数据到buffer中
response.sendBuffer(buffer, length);//直接发送指定长度的buffer
2.前端
对于前端数据,最简单的是使用下面代码来显示:
<img src="http://你的网址的接口" >
但是如果后端对应的接口的http的request的method不是GET就不能使用这个方法,或者如果需要获取response里面自定义的属性或者其它的数据就不能使用< img >标签。应该直接接受二进制数据:
var url = "你的网址的接口";
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.onload = function() {
if (this.status == 200) {
var blob = this.response;
console.log(this);
console.log(blob);
var img = document.createElement("img");
img.onload = function(e) {
window.URL.revokeObjectURL(img.src);
};
img.src = window.URL.createObjectURL(blob);
$("#imgcontainer").html(img);
}
}
xhr.send();
这样子返回就可以是二进制的数据,取决于前端想把数据保存为什么样子,然后也可以获取response里面的属性。