為了方便測試,我們這里寫了一個簡單的測試 web server,用來在網(wǎng)頁上向 API 服務器發(fā)請求。代碼很簡單,還是用 flask 來啟動 server,返回一個頁面。
上次我們一起完成了一個初級的API服務器的搭建,今天來給它增加點新功能,要看前面內(nèi)容的,??戳這里??
1.完善設計
在上次的設計當中,我們定義了三張表,AdminUser,用來作為調(diào)用 API 鑒權(quán)用戶,User,用來作為存儲普通用戶使用,Picture,用來作為存儲用戶上傳的圖片。但是當時只是實現(xiàn)了 AdminUser 的相關(guān)功能,而 User 和 Picture 還沒有真正的關(guān)聯(lián)起來,這次就把它們完善起來
2.測試 server
為了方便測試,我們這里寫了一個簡單的測試 web server,用來在網(wǎng)頁上向 API 服務器發(fā)請求。代碼很簡單,還是用 flask 來啟動 server,返回一個頁面。
@app.route('/jsupload', methods=['POST', 'GET'])
def jsupload():
return render_template('js_upload.html')
而返回的這個頁面也很簡單,包含一個 form,用來提交數(shù)據(jù)的。
<form id= "uploadForm">
<p >指定用戶名: <input type="text" name="filename" value= "" id="UID"/></p>
<p >上傳文件: <input type="file" name="file" id="PID"/></p>
<input type="button" value="上傳" onclick="upload()" />
</form>
服務起來之后大概是這樣的

不要嫌棄它丑,功能夠了就行了。
這樣一個簡單的測試server就好了。
3.前端代碼
前端代碼使用 Ajax 來提交數(shù)據(jù)和回顯數(shù)據(jù)
<script type="text/javascript">
function upload(){
//var formData = new FormData($( "#uploadForm" )[0]);
var img_file = document.getElementById("PID");
var fileObj = img_file.files[0];
var formData = new FormData();
formData.append("UID", $("#UID").val());
formData.append("PID", fileObj);
$.ajax({
method: "POST",
url: "http://127.0.0.1:9980/api/test",
timeout: 10000,
data: formData,
async: false,
headers: {"client-type":"platform",},
dataType: "json",
contentType:false,
processData:false,//數(shù)據(jù)不做預處理
success: function(res) {
return;
},
error: function(e){
alert(e.msg);
}
}).done(function(res){
var result = '';
var result1 = '';
result += '<img src="' + res['p_url'] + '" width="100">';
$('#result').html(result);
}
);
}
</script>
代碼都是比較基礎的,定位元素,調(diào)用函數(shù),說下這里的 url,那個就是我在本地啟動的 API server 的地址嘍。同時這里還在監(jiān)聽服務器的返回,獲取到返回的 p_url,來顯示圖片。
4. 后端代碼
首先判斷下前端傳的是否是圖片文件,如果不是直接返回錯誤
if 'PID' not in request.files:
return jsonify({'code': -1, 'filename': '', 'msg': 'please select one picture to upload'})
如果判斷通過,就獲取圖片和用戶名稱
user = request.form.get('UID')
f = request.files['PID']
然后在本地創(chuàng)建目錄用于保存圖片,并且著手處理 User 和 Picture 的關(guān)系
basepwd = os.getcwd()
pwd = os.path.join(basepwd, r'app')
tmp_path = os.path.join(pwd, r'static/%s' % user)
new_filename = str(time.time()) + '.' + f.filename.split('.')[1]
if not os.path.exists(tmp_path):
os.makedirs(tmp_path)
upload_path = os.path.join(tmp_path, secure_filename(new_filename))
f.save(upload_path)
if User.query.filter_by(username=user).first():
u = User.query.filter_by(username=user).first()
u.picture_count += 1
p = Picture(picture_name=user, picture=upload_path, picture_id=u.id)
db.session.add(u)
db.session.add(p)
db.session.commit()
如果不存在目錄則創(chuàng)建,并且保存圖片。如果用戶存在于數(shù)據(jù)庫中,那么 picture_count 加1,同時更新 Picture 表,關(guān)聯(lián) picture_id 為 user_id。
如果用戶不存在,那么先插入用戶,提交,然后再更新 Picture 表
newu = User(username=user)
db.session.add(newu)
db.session.commit()
newp = Picture(picture_name=user, picture=upload_path, picture_id=newu.id)
db.session.add(newp)
db.session.commit()
最后API返回p_url用于前端web展示
return jsonify({"p_url": 'http://127.0.0.1:9980/static/%s/' % user + new_filename})
5. 最終效果
最后的效果如下

同時在項目的 static 目錄下,會產(chǎn)生每個用戶的圖片,因為圖片的命名都使用了 time.time(),也就不存在重名覆蓋的問題啦

6. 任重道遠
這次的完善就到這里了,不過程序還是有很多問題的,比如已知的問題就有如果在web端不填寫名字或者不選擇圖片,都會產(chǎn)生一些問題;同時還可以增加一些接口,比如獲取用戶所有圖片等待,這些都留到后面再說吧
還有個嚴峻的問題,就是每次本地調(diào)測好之后,都要手動同步代碼到遠程服務器,非常之麻煩,雖然目前項目很小,但是 CI 還是很有必要的,后面就來聊聊怎么結(jié)合 GitHub 做持續(xù)集成吧
完整代碼看這里: https://github.com/zhouwei713/mini_api