上传头像跟上传文件一样,但是平时的应用中,我们都是直接点击一张图片,就会跳出类似于input标签中type为file的样式,这个实现就用到了label标签的特性,通过label标签的关联特性,我们可以将一张图片包在label标签中,并且将input标签隐藏掉,就可以实现点击图片,打开的是input的标签。
具体代码的实现:(基于bootstrap的实现)
<div>
<label class="col-sm-2 control-label">头像</label>
<div class="col-sm-8"> <label for="id_avatar"><img src="/static/avatars/default.png" alt="图像加载失败"></label> <input type="file" id="id_avatar" name="file" style="display:none"> </div>
</div>
实现了这个替换后,我们在使用时,就会发现,选择了图片后,不能显示,所以我们就要针对这个来实现预览功能
预览功能的实现:
<div class="form-group"> <label class="col-sm-2 control-label">头像</label> <div class="col-sm-8"> <label for="id_avatar"><img id="avatar-img" src="/static/img/default.png" alt=""></label> <input accept="image/*" type="file" name="avatar" id="id_avatar" style="display: none"> <span class="help-block"></span> </div> </div> <script src="/static/jquery-3.3.1.js"></script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> <script> // 找到头像的input标签绑定change事件 $("#id_avatar").change(function () { // 1. 创建一个读取文件的对象 var fileReader = new FileReader(); // 取到当前选中的头像文件 // console.log(this.files[0]); // 读取你选中的那个文件 fileReader.readAsDataURL(this.files[0]); // 读取文件是需要时间的 fileReader.onload = function () { // 2. 等上一步读完文件之后才 把图片加载到img标签中 $("#avatar-img").attr("src", fileReader.result); }; });
备注:
文件的读取是需要时间的,所以必须使用onload方法进行等待文件的读取完成
此外,使用this.files得到的是一个对象,取0得到的是这个文件。
我们也可以在输入文件的那个input框中添加一个属性:accept 比如accept="image/png",表示一个默认的格式。
预览功能实现后,当用户确认选择,进行上传时,就需要进行后端的存储操作
备注:使用form表单进行向后端的提交带文件的数据时,需要指定form表单的提交属性 在form表单中加上enctype属性 enctype=multipart/form-data,同样的使用ajax向后端提交带文件的数据时也需要类似的属性:1.添加两个属性processData:false,contentType:false 2.data必须是FormData()类型
以Django为例,使用ajax实现向后端的提交
ajax代码实现:
// AJAX提交注册的数据
$("#reg-submit").click(function () {
// 取到用户填写的注册数据,向后端发送AJAX请求
var formData = new FormData();
formData.append("username", $("#id_username").val());
formData.append("password", $("#id_password").val());
formData.append("re_password", $("#id_re_password").val());
formData.append("email", $("#id_email").val());
formData.append("avatar", $("#id_avatar")[0].files[0]);
formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());
$.ajax({
url: "/reg/",
type: "post",
processData: false,
contentType: false,
data: formData,
success:function (data) {
if (data.status){
// 有错误就展示错误
// console.log(data.msg);
// 将报错信息填写到页面上
$.each(data.msg, function (k,v) {
// console.log("id_"+k, v[0]);
// console.log($("#id_"+k));
$("#id_"+k).next("span").text(v[0]).parent().parent().addClass("has-error");
})
}else {
// 没有错误就跳转到指定页面
location.href = data.msg;
}
}
})
});
// 将所有的input框绑定获取焦点的事件,将所有的错误信息清空
$("form input").focus(function () {
$(this).next().text("").parent().parent().removeClass("has-error");
})
视图函数部分的实现:
# 注册的视图函数 def register(request): if request.method == "POST": ret = {"status": 0, "msg": ""} form_obj = forms.RegForm(request.POST) print(request.POST) # 帮我做校验 if form_obj.is_valid(): # 校验通过,去数据库创建一个新的用户 form_obj.cleaned_data.pop("re_password") avatar_img = request.FILES.get("avatar") models.UserInfo.objects.create_user(**form_obj.cleaned_data, avatar=avatar_img) ret["msg"] = "/index/" return JsonResponse(ret) else: print(form_obj.errors) ret["status"] = 1 ret["msg"] = form_obj.errors print(ret) print("=" * 120) return JsonResponse(ret) # 生成一个form对象 form_obj = forms.RegForm() print(form_obj.fields) return render(request, "register.html", {"form_obj": form_obj})
备注:上传的文件名有重复时,Django内部会自动在文件名后加一个随机的字符串做区分,并在数据库存储。
另外,在Django中我们用到了form组件的一些用法
from django import forms from blog import models from django.core.exceptions import ValidationError # from django.core.validators import RegexValidator # mobile = forms.CharField(validators=[RegexValidator('regex','哈哈哈')]) class RegForm(forms.Form): username=forms.CharField(label='用户名',max_length=12,error_messages={'required':'不能为空'},widget=forms.widgets.TextInput(attrs={'class':'form-control','placeholder':'请输入用户名'})) password = forms.CharField(label='密码',min_length=6,error_messages={'min_length':'密码最少为六位','required':'不能为空'},widget=forms.widgets.PasswordInput(attrs={'class':'form-control','placeholder':'请输入密码'})) re_password = forms.CharField(label='确认密码',min_length=6,error_messages={'min_length':'确认密码最少为六位','required':'不能为空'},widget=forms.widgets.PasswordInput(attrs={'class':'form-control','placeholder':'请输入确认密码'})) email = forms.EmailField(label='邮箱',error_messages={'required':'不能为空','invalid':'格式错误'},widget=forms.widgets.EmailInput(attrs={'class':'form-control','placeholder':'请输入邮箱'})) def clean(self): password = self.cleaned_data.get('password') re_password = self.cleaned_data.get('re_password') if re_password: if password !=re_password: self.add_error('re_password',ValidationError('两次密码不一致')) else: self.cleaned_data.pop('re_password') return self.cleaned_data def clean_username(self): username = self.cleaned_data.get('username') is_exists = models.UserInfo.objects.filter(username=username) if is_exists: self.add_error('username',ValidationError('用户名已存在')) return username def clean_email(self): email = self.cleaned_data.get('email') is_exist = models.UserInfo.objects.filter(email=email) if is_exist: self.add_error('email',ValidationError('邮箱已存在')) else: return email
备注:label标签的特殊用法:
<!--label的两种用法--> <!--用法一:正常用法 实现关联--> <label for="d1">用户:</label> <input type="text" id="d1"> <!--用法二:将input标签包到label标签里面也可以实现联动效果--> <label>用户名 <input type="text"></label>
用户上传的文件,Django都叫media文件
如果要在页面中展示出头像,如果上传文件的路径在static目录下,就会在页面展示出来,否则,就无法展示,因为settings.py 文件中没有设置文件访问路径。
如下图:这样配置上传文件的路径,就可以直接在页面中展示出来
如果放在了其他路径下,就要一些相应的配置
Django给我们提供了这种功能,只需要配置好media就可以
1. settings.py中:
#BASE_DIR是Django中的项目的绝对路径
#只需要在settings中添加这些就可以
MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR,'media')
2.路由 urls.py 中的配置:
from django.views.static import serve from django.conf import settings #在路由中添加路径 url(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),