php input file ajax,jquery ajax put file, php save file
在項目中第一次遇到這個問題,當我在更新用戶頭像的時候,選擇使用put上傳頭像,然而服務(wù)端并不能通過$_FILES像我們以前一樣得到想要的結(jié)果。這是一個相當復(fù)雜的問題,我們這篇文章就來試圖解決這個問題。
restful post vs. put
如果我們希望嚴格保持restful風(fēng)格,那么一定要遵循post=add, put=update的風(fēng)格,在stackoverfollow上已經(jīng)有很多這樣的討論,所以,就不這里再討論。但是restful不是規(guī)范,我們并不一定非得嚴格按照這個規(guī)定去做,在適當?shù)臅r候,其實我們確實可以選擇使用post代替put.
為什么仍然堅持put
在PHP接受數(shù)據(jù)傳遞時,只有$_GET, $_POST,而$_PUT和$_DELETE是沒有的,當然,由于delete操作往往把要刪除的資源直接在url中加以確定。所以,現(xiàn)在其實最麻煩的,是put操作。
我們有足夠的理由使用put,因為我們在接受put信息時,采用的是php://input的形式,這也就是說,通過
file_get_contents('php://input')
可以獲取傳遞內(nèi)容的原始信息,以及二進制流,這使得傳輸和接受都是非常高效率的。我們甚至可以直接通過put傳遞文件的二進制流,在服務(wù)端保存這個二進制信息,就得到了原始的文件。這個過程不會在服務(wù)器上產(chǎn)生臨時文件,不會消耗更多的內(nèi)存。
如何獲取put信息
但是,隨之而來的是,解析put信息成為讓人非常煩惱的事,我們通過網(wǎng)上(官網(wǎng))的一些示例代碼,發(fā)現(xiàn)也只能解決部分問題,比如當我們通過發(fā)送put信息到服務(wù)端更新文章信息時,由于文章信息不包含復(fù)雜信息,服務(wù)端通過官方示例就可以得到對應(yīng)的信息,而這樣的到的信息,和$_POST沒有什么不同。
然而,如果發(fā)送的信息中如果含有文件,則情況將會大不一樣。
在$_POST文件時,要求表單含有enctype='multipart/form-data'屬性,而以往我們長長不去思考它的作用,現(xiàn)在我們才知道,這其實時一中表單請求的加密形式,當表單含有該屬性,提交的內(nèi)容中含有文件時,服務(wù)器將會按照該規(guī)則進行解碼,而解碼的到的文件信息,將會以$_FILES返回。所以,當我們在使用post進行文件上傳的時候,實際上,文件不能到$_POST中去取,而是到$_FILES中。
put傳輸?shù)氖嵌M制信息流,因此不需要服務(wù)端解碼,自然也就不存在$_FILES的具體信息(為空)。這下我們就懵了,那上傳的文件到哪里去了(我們知道$_FILES中的文件保存在服務(wù)器臨時目錄中,需要我們通過move_uploaded_file函數(shù)進行轉(zhuǎn)移)?
實際上,put的文件哪里都沒有去,還在php://input原始信息流中,我們要做的,是對php://input進行解碼,得到我們想要的文件信息。
我們來看put得到的結(jié)果:
echo file_get_contents('php://input');
exit;
一般而言,我們可以得到兩種結(jié)果,
一種:不包含文件的
這時我們長長得到的,是一串字符串,例如name=Jerry&age=18,我們在大部分PHP中如此去解析:
parse_str(file_get_contents('php://input'), $_PUT);
另一種:包含文件的
這種情況是比較復(fù)雜的,我們會發(fā)現(xiàn),php://input是一個復(fù)雜的體系。
------------------------------b2449e94a11cContent-Disposition:form-data;name="user_id"3------------------------------b2449e94a11cContent-Disposition:form-data;name="post_id"
5------------------------------b2449e94a11cContent-Disposition:form-data;name="image";filename="/tmp/current_file"Content-Type:application/octet-stream�����JFIF���������...a bunch of binary data
可以看到上面的結(jié)果中,用------------------------------b2449e94a11c把整個內(nèi)容分為了幾個部分,其中第三個部分是一個文件。
經(jīng)過觀察,我們最終決定,用正則分析它,并得到我們想要的結(jié)果,最后我參考一些網(wǎng)上的朋友給出的代碼,總結(jié)了下面這個函數(shù):
/** * Parse raw HTTP request data
*
* Pass in $a_data as an array. This is done by reference to avoid copying
* the data around too much.
*
* Any files found in the request will be added by their field name to the
* $data['files'] array. * * https://www.tangshuang.net/?p=2294
*
* @param array Empty array to fill with data
* @return array Associative array of request data
*/
function parse_http_input_raw() {
$a_data = array();
// read incoming data
$input = file_get_contents('php://input');
// grab multipart boundary from content type header
preg_match('/boundary=(.*)$/', $_SERVER['CONTENT_TYPE'], $matches);
// content type is probably regular form-encoded
if (!count($matches)) {
// we expect regular puts to containt a query string containing data
parse_str(urldecode($input), $a_data);
return $a_data;
}
$boundary = $matches[1];
// split content by boundary and get rid of last -- element
$a_blocks = preg_split("/-+$boundary/", $input);
array_pop($a_blocks);
// loop data blocks
foreach ($a_blocks as $id => $block) {
if (empty($block)) continue;
// you'll have to var_dump $block to understand this and maybe replace or \r with a visibile char
// parse uploaded files
if (strpos($block, 'filename=') !== FALSE) {
// match "name", then everything after "stream" (optional) except for prepending newlines
preg_match("/name=\"([^\"]*)\".*filename=\"([^\"].*?)\".*Content-Type:\s+(.*?)[|\r|\r]+([^\r].*)?$/s", $block, $matches);
$a_data['files'][$matches[1]] = array('name' => $matches[1],'filename' => $matches[2],'type' => $matches[3],'blob' => $matches[4]);
}
// parse all other fields
else {
// match "name" and optional value in between newline sequences
preg_match('/name=\"([^\"]*)\"[|\r]+([^\r].*)?\r$/s', $block, $matches);
print_r($matches);
$a_data[$matches[1]] = $matches[2];
}
}
return $a_data;
}
所有的文件,被放在一個字段中提供使用:
$_PUT = parse_http_input_raw();
注意,文件的二進制流還在$_PUT中呢。我們需要通過遍歷$_PUT['files']保存文件。
jquery ajax如何去發(fā)起put請求和上傳
這個是最簡單的了,可以看我之前寫過的一些文章,非常詳細,這里僅給出核心代碼:
$.ajax({
url: url,
type: 'PUT',
data: data,
success: function(result) {}
});
僅此而已。但是,當我們需要上傳文件或圖片的時候,就相對復(fù)雜一些,因為無論是POST或PUT,ajax都不會主動把表單中的文件流上傳,而是只提交其他類型的數(shù)據(jù)。
我們需要借助FormData()來實現(xiàn)文件的ajax上傳:
$('#form').on('submit',function(e) {
e.preventDefault();
var $form = $(this), $files = $form.find('input[type=file][name]'), data = new FormData();
$files.each(function() {if ('files' in this && this.files.length > 0) {data.append(this.name, this.files[0]);}});
// 省略了把非文件數(shù)據(jù)加入到data中
$.ajax({
url: 'your url',
type: 'PUT',
data: data,
contentType: false,
processData: false,
success: function(result) {}
});
});
可以看到,這個過程也很簡單,并沒有想象到那么復(fù)雜。通過new FormData()產(chǎn)生的對象,通過遍歷form中的input或其他數(shù)據(jù)對象,將結(jié)果逐個加入到data中。new FormData()有一個append方法,可以實現(xiàn)append(key,value),從而將你需要的數(shù)據(jù)加入到data中,ajax提交到時候,data則是全部的有效數(shù)據(jù)(包括文件的二進制流)。
(完)
2016-03-26
7204
總結(jié)
以上是生活随笔為你收集整理的php input file ajax,jquery ajax put file, php save file的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 事务超时时间无效_什么是ZooKeepe
- 下一篇: java中项目启动时加载_如何在项目启动