4

追加の検証を行うためにフォームが送信されたときに実行したい単純な js/jquery 関数があります。私のフォームでは複数のファイルを入力できます。すべてのファイルの合計が、設定したファイル制限を超えないようにしたいと考えています。

var totalFileSize = 0;
$("input:file").each(function () {
    var file = $(this)[0].files[0];
    if (file) totalFileSize += file.size;
});

return totalFileSize < maxFileSize; // I've set maxFileSize elsewhere.

問題は、これを jquery 検証の一部として実行したいということです。私のフォームは、他の場所では、標準の MVC3 検証と、私が書いた別のカスタム控えめな検証を使用しています。このファイル サイズ バリデータが失敗した場合、他の jquery バリデータと同じように動作するようにします。明らかに、送信を停止し、他のバリデータと同じ概要ボックスにエラー メッセージを表示したいと考えています。

送信時の検証の一部として、このような単純なメソッドを呼び出す方法はありますか? $.validator.addMethod について考えましたが、それを各 input:file 要素に追加すると、送信時に同じバリデーターが複数回実行され、エラー メッセージが複数回表示されます。このバリデータを追加する方法があれば、それをどの要素にも関連付けないでください。

4

1 に答える 1

10

カスタム検証属性を記述し、カスタム クライアント側アダプターを登録できます。詳しく説明しましょう。

アップロードするファイルのリストを表すビュー モデルがあり、アップロードするすべてのファイルの合計サイズを 2 MB に制限したいとします。あなたのビューモデルは確かに次のように見えるかもしれません:

public class MyViewModel
{
    [MaxFileSize(2 * 1024 * 1024, ErrorMessage = "The total file size should not exceed {0} bytes")]
    public IEnumerable<HttpPostedFileBase> Files { get; set; }
}

[MaxFileSize]ここで、明らかにサーバー側の検証を実行するこのカスタム検証属性を定義しましょうが、それに加えて、 IClientValidatableインターフェイスを実装して、この検証ロジックをクライアントに転置できるようにするカスタムの目立たないクライアント側検証ルールを登録できるようにします (明らかにHTML5 File APIをサポートするブラウザの場合、選択したファイルのサイズをクライアントで判断できます => IE は、このようなものやこのブラウザを使用するユーザーにとってはまったく問題外ですサーバー側のみの検証でそれらを満足させるか、何か良いことをする必要があります-この平和なソフトウェアが実行できるこの世界で唯一の有用なタスクにはInternet Explorerを使用します。Windowsをクリーンインストールしたら、インターネットを介して実際のファイルをダウンロードしますウェブブラウザ):

public class MaxFileSizeAttribute : ValidationAttribute, IClientValidatable
{
    public MaxFileSizeAttribute(int maxTotalSize)
    {
        MaxTotalSize = maxTotalSize;
    }

    public int MaxTotalSize { get; private set; }

    public override bool IsValid(object value)
    {
        var files = value as IEnumerable<HttpPostedFileBase>;
        if (files != null)
        {
            var totalSize = files.Where(x => x != null).Sum(x => x.ContentLength);
            return totalSize < MaxTotalSize;
        }

        return true;
    }

    public override string FormatErrorMessage(string name)
    {
        return base.FormatErrorMessage(MaxTotalSize.ToString());
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = FormatErrorMessage(MaxTotalSize.ToString()),
            ValidationType = "maxsize"
        };
        rule.ValidationParameters["maxsize"] = MaxTotalSize;
        yield return rule;
    }
}

次のステップは、コントローラーを用意することです。

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel());
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        if (!ModelState.IsValid)
        {
            // Server side validation failed => redisplay the view so
            // that the user can fix his errors
            return View(model);
        }

        // Server side validation passed => here we can process the
        // model.Files collection and do something useful with the 
        // uploaded files knowing that their total size will be smaller
        // than what we have defined in the custom MaxFileSize attribute
        // used on the view model
        // ...

        return Content("Thanks for uploading all those files");
    }
}

および対応するビュー:

@model MyViewModel

<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
    (function ($) {
        $.validator.unobtrusive.adapters.add('maxsize', ['maxsize'], function (options) {
            options.rules['maxsize'] = options.params;
            if (options.message) {
                options.messages['maxsize'] = options.message;
            }
        });

        $.validator.addMethod('maxsize', function (value, element, params) {
            var maxSize = params.maxsize;
            var $element = $(element);
            var files = $element.closest('form').find(':file[name=' + $element.attr('name') + ']');
            var totalFileSize = 0;
            files.each(function () {
                var file = $(this)[0].files[0];
                if (file && file.size) {
                    totalFileSize += file.size;
                }
            });
            return totalFileSize < maxSize;
        }, '');
    })(jQuery);
</script>


@Html.ValidationMessageFor(x => x.Files)
@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <div>
        @foreach (var item in Enumerable.Range(1, 3))
        {
            @Html.TextBoxFor(x => x.Files, new { type = "file" })
        }
    </div>
    <button type="submit">OK</button>
}

明らかに、ここに示されている JavaScript は、ビュー内では何の関係もありません。ビューが参照できる別の再利用可能な JavaScript ファイルに入れる必要があります。ここでは、読みやすさとシナリオの再現を容易にするためにインラインで含めていますが、実際にはインライン JavaScript を記述することはありません。

于 2012-06-11T21:25:06.583 に答える