jquery使用ajax

0202年了, jQuery可以继续梭哈...
记录一下jQuery使用ajax上传文件Post表单的操作

相关文档: jQuery官方ajax文档

上传文件

仅上传单个文件

前端jquery使用FormData构造请求数据

<!-- 防止点击提交按钮页面自动刷新, 所以设置 onsubmit="return false;" -->
<form onsubmit="return false;" method="post" class="form-group">
    <div>
        <input type="file" id="i-avatar" name="image" />
    </div>
    <div class="mt-2">
        <button class="btn btn-primary" onclick="uploadImage()">提交</button>
    </div>
</form>

<script type="text/javascript">
    function uploadImage() {
        const form = new FormData();
        form.append("file", $("#i-avatar").prop("files")[0]);
        $.ajax({
            type: "post",
            url: "/videos/image",
            contentType: false,
            data: form,
            processData: false,
            success: function () {
                window.alert("success");
            }
        });
    }
</script>

后端Action:

[HttpPost("image")]
public async Task<IActionResult> UploadImage([FromForm] IFormFile file)
{
    using (var rs = file.OpenReadStream())
    {
        string path = Path.Combine(_webHostEnvironment.WebRootPath, "upload", "images", file.FileName);
        Directory.CreateDirectory(Path.GetDirectoryName(path));
        using (var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
        {
            await rs.CopyToAsync(fs);
            await fs.FlushAsync();
        }
    }

    return Ok();
}

这里就是拿到文件流然后保存到~/wwwroot/upload/image文件夹下, 这里文件名直接使用了用户上传的文件名, 微软官方文档中不建议这么做, 因为可能会有安全问题

写好之后在浏览器中测试, 看一下最终发出的请求是什么样的

alt

可以看到, 虽然我们再ajax函数中将contentType设置成了false, 但是浏览器自动为我们添上了 content-type: multipart/form-data; boundary=----WebKitFormBoundaryxZyRmmXUfCxJRX7A, 一开始我手动设置了contentType: multipart/form-data, 这样肯定是不行的, 至于为什么, 因为没有设置boundary这个东西啊

简单总结一下ajax要注意的配置

dataType, 表示期望服务器返回的类型, 这个不能乱填, 如果返回的格式是json则设置为 json, 返回的是纯文字, 则设置为 text, 这个属性还可以被设置为 xml, html, script, jsonp, 如果不设置这个属性, 那么jQuery会根据响应的MIME来自动推断, 如果你不确定响应的格式, 那么就不要设置这个属性;

contentType, 发送数据给服务器时, 指定的数据的格式, 注意, 一定要注意, 这个属性是有默认值的, 且默认值是 application/x-www-form-urlencoded; ciharset=UTF-8, 如果服务器不接受这种类型的输入, 那么就会请求失败, 所以务必确认好再设置, 如果将这个属性设置为false, 那么jquery就不会在请求头的加入content-type

processData, 告诉jquery, 是否要将请求数据内容转换成 application/x-www-form-urlencoded, 默认是true, 也就是说jquery会将ajax的data属性格式化成 application/x-www-form-urlencoded 这种数据格式, 关于Post请求的数据格式的问题, 可以看这篇博文, HTTP_POST请求的数据格式, 非常详细, 如果是上传文件的场景, 要将这个属性设置成 false

上传文件的同时, 再传一些其他 Key-Value 类型数据

在用户注册的场景下, 需要提交用户名, 密码和头像等信息, 头像往往是一个图片文件; 当然, 也可以先单独上传头像, 然后传图片的url给用户注册接口api, 但是, 我觉得更好的做法是直接把文件一起传给用户注册接口的api, 这个接口同时保存用户头像;

还是使用FormData对象来构造请求数据:

<form onsubmit="return false;">
    <div>
        <label>
            用户名:&nbsp;<input type="text" id="username" name="username" value="" />
        </label>
    </div>
    <div>
        <label>
            密码:&nbsp;<input type="password" id="password" name="password" value="" />
        </label>
    </div>
    <div>
        <label>
            头像:&nbsp;<input type="file" id="avatar" name="avatar" value="" multiple/>
        </label>
    </div>
    <div><button class="btn btn-primary" onclick="registerUser()">注册</button></div>
    <div id="register-result"></div>
</form>

<script type="text/javascript">
    function registerUser() {
        const formData = new FormData();
        formData.set("username", $("#username").val());
        formData.set("password", $("#password").val());
        const files = $("#avatar").prop("files");
        if (files && files.length > 0)
            formData.set("avatar", files[0]);
        // 如果有多个文件怎么办?
        //for (let i = 0; i < files.length; i++) {
        //    formData.set(i, files[i]);
        //}
        $.ajax({
            url: "/user",
            type: "post",
            processData: false,
            contentType: false,
            data: formData,
            success: function (result) {
                $("#register-result").text(JSON.stringify(result));
            }
        });
    }
</script>

后台接收数据:

[HttpPost]
public Task<IActionResult> Register([FromForm] IFormCollection form)
{
    return Task.FromResult<IActionResult>(Ok(form));
}

测试看一下请求是什么样的

alt

可以看到 content-type 依旧是 multipart/formdata; boundary...这种, 但是, 如果我要在传图片的同时, 传一些其他数据数据或者复杂对象怎么办啊? 比如我后台定义这样的对象:

public class VideoTagDto
{
    public string TagName { get; set; }
    public DateTime CreateDate { get; set; }
}

public class ActorDto
{
    public string Name { get; set; }
    public string Sex { get; set; }
    public string Country { get; set; }
    public DateTime Birth { get; set; }
}

public class VideoDto
{
    public string Name { get; set; }
    public DateTime PublishDate { get; set; }
    public IList<VideoTagDto> VideoTags { get; set; }
    public IList<ActorDto> Actors { get; set; }
    public IFormFile CoverImage { get; set; }

    public VideoDto()
    {
        VideoTags = new List<VideoTagDto>();
        Actors = new List<ActorDto>();
    }
}

前台有没有办法可以让后台接口接收到 VideoDto 呢? VideoDto 里面包含了 IFormFile 类型 CoverImage;

不好意思, 好像不太可以, 我尝试了希望使用 application/json 格式来上传文件和其他复杂对象, 不过浏览器的File对象没办法序列化成JSON格式; 所以只能先上传文件, 然后拿到文件的url, 再和其他数据一起上传

posted @ 2020-08-01 15:11  Laggage  阅读(361)  评论(0编辑  收藏  举报