Javascript 使用Ajax调用上载大文件

Javascript 使用Ajax调用上载大文件,javascript,microsoft-graph-api,onedrive,Javascript,Microsoft Graph Api,Onedrive,我正在尝试使用MicrosoftGraphAPI通过Ajax调用上传大文件 根据,您首先必须创建一个上载会话,我可以用我的代码成功地完成该会话。当我开始上传到返回的uploadUrl时,问题就出现了。我得到以下错误: { 代码:“invalidRequest”, 消息:“内容范围标头长度与提供的字节数不匹配。” } 因此,当我在Fiddler中检查实际请求时,我可以看到Content Length头被设置为0 因此,我尝试将我的内容长度标题设置为我要发送的数组缓冲区的大小,但我收到一个错误(C

我正在尝试使用MicrosoftGraphAPI通过Ajax调用上传大文件

根据,您首先必须创建一个上载会话,我可以用我的代码成功地完成该会话。当我开始上传到返回的
uploadUrl
时,问题就出现了。我得到以下错误:

{
代码:“invalidRequest”,
消息:“内容范围标头长度与提供的字节数不匹配。”
}
因此,当我在Fiddler中检查实际请求时,我可以看到
Content Length
头被设置为
0

因此,我尝试将我的
内容长度
标题设置为我要发送的
数组缓冲区
的大小,但我收到一个错误(Chrome),上面显示:

拒绝设置不安全的标题“内容长度”

我已经为此挣扎了整整两天了,我已经束手无策了。关于MicrosoftGraphAPI的文档很少,适合我尝试做的例子更少

我无法想象我是唯一一个尝试这样做的人,我认为这是一个相当普遍的想法

下面是我正在使用的代码。我在其他地方获得了我的
AccessToken
URL
,但它们似乎很好,因为我可以从控制台使用它们进行查询

this.UploadLargeFileToFolderID=函数(FolderID,
文件对象,
显示加载消息,
成功功能,
错误函数,
CompleteFunction){//将一个最大为60Mb的文件上载到文件夹。
//显示加载消息
ShowLoadingMessage&&ThisRepository.LoadingMessage.Show();
//将文件名清除为SharePoint可接受的名称
FileObject.name=cleannedrivefilename(FileObject.name);
var UploadSessionURL=FolderID?
ThisRepository.RepositoryRootURL+'/drive/items/'+FolderID+'/createUploadSession':
ThisRepository.RepositoryRootURL+'/drive/root/createUploadSession';
//首先,获取上传Sesion。
$.ajax({
url:UploadSessionURL,
方法:“POST”,
标题:{
授权:“承载人”+ThisRepository.AccessToken
},
成功:函数(数据、文本状态、jqXHR){
//已成功获取上载会话。
log('Session:');
控制台日志(数据);
//创建ArrayBuffer并上载文件。
ReturnArrayBufferFromFile(文件对象,函数(ArrayBuffer){
log('Array Buffer:');
console.log(ArrayBuffer);
var MaxChunkSize=327680;
var ChunkSize=ArrayBuffer.bytellength
用于返回要发送的数组缓冲区的函数

函数ReturnArrayBufferFromFile(InputFile,CallBackFunction){
log(“输入文件”);
console.log(输入文件);
var FileName=cleannedrivefilename(InputFile.name);
var FileUploadReader=newfilereader();
if(InputFile.type.match('image.*')){
//如果文件是图像,我们要确保
//在我们归还之前,它不会太大。
FileUploadReader.onloadend=函数(e){
var img=新图像();
//将图像大小调整为最大200万像素。
img.onload=函数(){
var MAX_HEIGHT=2048;//最大最终高度,以像素为单位
var MAX_WIDTH=2048;//最大最终宽度,以像素为单位
var高度=img高度;
变量宽度=img.width;
//请重新调整大小
如果(宽度>高度){//横向图像
如果(宽度>最大宽度){
高度*=最大宽度/宽度;
宽度=最大宽度;
};
}else{//肖像图像
如果(高度>最大高度){
宽度*=最大高度/高度;
高度=最大高度;
};
};
//创建一个新的画布元素,该元素的大小与图像的大小一致
var canvas=document.createElement(“canvas”);
画布宽度=宽度;
canvas.height=高度;
canvas.getContext('2d').drawImage(this,0,0,width,height);
//为上载功能创建新的文件读取器。
var ConvertedFile=canvas.toBlob(函数(blob){
var ConvertedFileReader=newfilereader();
ConvertedFileReader.onloadend=函数(loadendevent){
//返回loadendevent.target.result;
var结果=loadendevent.target.result;
var Rawresult=result.split(',')[1];
CallBackFunction(loadendevent.target.result);
};
ConvertedFileReader.readAsArrayBuffer(blob);
}“图像/jpeg”,0.90);
};
img.src=e.target.result;
};
this.UploadLargeFileToFolderID = function (FolderID, FileObject, ShowLoadingMessage, SuccessFunction, ErrorFunction, CompleteFunction, ProgressFunction) {//will upload a file up to 60Mb to folder.
    ShowLoadingMessage && ThisRepository.LoadingMessage.Show(); //shows the loading message

    FileObject.name = CleanOneDriveFileName(FileObject.name); //cleans the file name to something acceptable to SharePoint
    var NewFileName = CleanOneDriveFileName(FileObject.name);
    var UploadSessionURL = FolderID ? ThisRepository.RepositoryRootURL + '/drive/items/' + FolderID + ':/' + NewFileName + ':/createUploadSession' : ThisRepository.RepositoryRootURL + '/drive/root:/' + NewFileName + ':/createUploadSession';
    var PathToParent = FolderID ? ThisRepository.RepositoryRootURL + '/drive/items/' + FolderID + ':/' + NewFileName + ':/' : ThisRepository.RepositoryRootURL + '/drive/root:/' + NewFileName + ':/'; //used if we have a naming conflict and must rename the object.

    var UploadSessionOptions = {
        item: {
            //"@microsoft.graph.conflictBehavior": "rename",
            "@microsoft.graph.conflictBehavior": "replace",
        }
    };

    //First, get the Upload Sesion.
    $.ajax({
        url: UploadSessionURL,
        method: 'POST',
        headers: { authorization: "Bearer " + ThisRepository.AccessToken, 'Content-Type': 'application/json', 'accept': 'application/json'},
        data: JSON.stringify(UploadSessionOptions),
        success: function (SessionData, textStatus, jqXHR) { //successfully got the upload session.
            //Create the ArrayBuffer and upload the file.
            ReturnArrayBufferFromFile(FileObject,
                    function (ArrayBuffer) {
                    var uInt8Array = new Uint8Array(ArrayBuffer);
                    var FileSize = uInt8Array.length;
                    var MaxChunkSize = 3276800; //approx 3.2Mb.  Microsoft Graph OneDrive API says that all chunks MUST be in a multiple of 320Kib (327,680 bytes).  Recommended is 5Mb-10Mb for good internet connections.  
                    var ChunkSize = FileSize < MaxChunkSize ? FileSize : MaxChunkSize;
                    var DataUploadURL = SessionData.uploadUrl;

                    chunkedUpload(DataUploadURL, uInt8Array, ChunkSize, 0, null, null, null,
                        function (progress) { //progress handler
                            ProgressFunction(progress);
                        },
                        function (response) { //completion handler
                    if (response.StatusCode == 201 || response.StatusCode == 200) {//success.  201 is 'Created' and 200 is 'OK'

                        typeof SuccessFunction === 'function' && SuccessFunction(response);

                        ThisRepository.LoadingMessage.Remove();
                        typeof CompleteFunction === 'function' && CompleteFunction(response);

                    } else if (response.StatusCode == 409) { //naming conflict?

                        //if we had a renaming conflict error, per Graph Documentation we can make a simple PUT request to rename the file

                        //HAVE NOT SUCCESSFULLY TESTED THIS...
                        var NewDriveItemResolve = {
                            "name": NewFileName,
                            "@microsoft.graph.conflictBehavior": "rename",
                            "@microsoft.graph.sourceUrl": DataUploadURL
                        };

                        $.ajax({
                            url: PathToParent,
                            method: "PUT",
                            headers: { authorization: "Bearer " + ThisRepository.AccessToken, 'Content-Type': 'application/json', accept: 'application/json' },
                            data: JSON.stringify(NewDriveItemResolve),
                            success: function (RenameSuccess) {
                                console.log(RenameSuccess);
                                typeof SuccessFunction === 'function' && SuccessFunction(response);

                                ThisRepository.LoadingMessage.Remove();
                                typeof CompleteFunction === 'function' && CompleteFunction(response);

                            },
                            error: function (RenameError) {
                                console.log(RenameError);

                                var CompleteObject = { StatusCode: RenameError.status, ResponseObject: RenameError, Code: RenameError.error ? RenameError.error.code : 'Unknown Error Code', Message: RenameError.error ? RenameError.error.message : 'Unknown Error Message' };
                                var Status = CompleteObject.StatusCode;
                                var StatusText = CompleteObject.Code;
                                var ErrorMessage = CompleteObject.Message;

                                var ErrorMessage = new Alert({ Location: ThisRepository.LoadingMessage.Location, Type: 'danger', Text: "Status: " + Status + ': ' + StatusText + "<br />Error: " + ErrorMessage + '<br />Rest Endpoint: ' + data.uploadUrl });
                                ErrorMessage.ShowWithoutTimeout();

                                typeof ErrorFunction == 'function' && ErrorFunction(response);

                                ThisRepository.LoadingMessage.Remove();
                                typeof CompleteFunction === 'function' && CompleteFunction(response);

                            },
                            complete: function (RenameComplete) { /* Complete Function */ }

                        });


                    } else { //we had an error of some kind.

                        var Status = response.StatusCode;
                        var StatusText = response.Code;
                        var ErrorMessage = response.Message;

                        var ErrorMessage = new Alert({ Location: ThisRepository.LoadingMessage.Location, Type: 'danger', Text: "Status: " + Status + ': ' + StatusText + "<br />Error: " + ErrorMessage + '<br />Rest Endpoint: ' + data.uploadUrl });
                        ErrorMessage.ShowWithoutTimeout();

                        //CANCEL THE UPLOAD SESSION.
                        $.ajax({
                            url: UploadSessionURL,
                            method: "DELETE",
                            headers: { authorization: "Bearer " + ThisRepository.AccessToken },
                            success: function (SessionCancelled) { console.log('Upload Session Cancelled');},
                            error: function (SessionCancelError) { /* Error Goes Here*/},
                        });

                        typeof ErrorFunction == 'function' && ErrorFunction(response);

                        ThisRepository.LoadingMessage.Remove();
                        typeof CompleteFunction === 'function' && CompleteFunction(response);
                    };                      
                });
                }
            );
        },
        error: function (jqXHR, textStatus, errorThrown) {
            console.log('Error Creating Session:');
            console.log(jqXHR);
            //WE MAY HAVE A CANCELLED UPLOAD...TRY TO DELETE THE OLD UPLOAD SESSION, TELL THE USER TO TRY AGAIN.
            //COULD OPTIONALLY RUN A "RENAME" ATTEMPT HERE AS WELL
            $.ajax({
                url: PathToParent,
                method: "DELETE",
                headers: { authorization: "Bearer " + ThisRepository.AccessToken },
                success: function (SessionCancelled) { console.log('Upload Session Cancelled'); },
                error: function (SessionCancelError) { console.log(SessionCancelError); },
            });

            typeof ErrorFunction === 'function' && ErrorFunction(jqXHR);
        },
        complete: function (jqXHR, textStatus) { /* COMPLETE CODE GOES HERE */},
    });   
};

function ReturnArrayBufferFromFile(InputFile, CallBackFunction) {
var FileName = InputFile.name;
var FileUploadReader = new FileReader();

//Check the file type.  If it's an image, we want to make sure the user isn't uploading a very high quality image (2 megapixel max for our purposes).

if (InputFile.type.match('image.*')) { // it's an image, so we will resize it before returning the array buffer...
    FileUploadReader.onloadend = function (e) {
        var img = new Image();

        img.onload = function () { //will resize an image to a maximum of 2 megapixels.

            var MAX_HEIGHT = 2048;//max final height, in pixels
            var MAX_WIDTH = 2048; //max final width, in pixels
            var height = img.height;
            var width = img.width;

            //do the resizing
            if (width > height) {//landscape image
                if (width > MAX_WIDTH) {
                    height *= MAX_WIDTH / width;
                    width = MAX_WIDTH;
                };
            }
            else { //portrait image
                if (height > MAX_HEIGHT) {
                    width *= MAX_HEIGHT / height;
                    height = MAX_HEIGHT;
                };
            };

            //Create a new canvas element, correctly sized with the image
            var canvas = document.createElement("canvas");
            canvas.width = width;
            canvas.height = height;
            canvas.getContext('2d').drawImage(this, 0, 0, width, height);

            //Create the new file reader for the upload function.                   
            var ConvertedFile = canvas.toBlob(function (blob) {
                var ConvertedFileReader = new FileReader();

                ConvertedFileReader.onloadend = function (loadendevent) { //return the ArrayBuffer
                    CallBackFunction(loadendevent.target.result);
                };

                ConvertedFileReader.readAsArrayBuffer(blob);
                //ConvertedFileReader.readAsDataURL(blob);


            }, 'image/jpeg', 0.90);
        };

        img.src = e.target.result;
    };

    FileUploadReader.readAsDataURL(InputFile);
}
else {
    FileUploadReader.onloadend = function (e) {//File is not an image.  No pre-work is required.  Just return as an array buffer.
        CallBackFunction(e.target.result);
    };
    FileUploadReader.readAsArrayBuffer(InputFile);
};
};

function chunkedUpload(url, file, chunkSize, chunkStart, chunkEnd, chunks, 
    chunksDone, ProgressCallBack, CompleteCallBack) {

var filesize = file.length;


chunkSize = chunkSize ? chunkSize : 3276800; //note:  Microsoft Graph indicates all chunks MUST be in a multiple of 320Kib (327,680 bytes).  
chunkStart = chunkStart ? chunkStart : 0;
chunkEnd = chunkEnd ? chunkEnd : chunkSize;
chunks = chunks ? chunks : Math.ceil(filesize / chunkSize);
chunksDone = chunksDone ? chunksDone : 0;
console.log('NOW CHUNKS DONE = ' + chunksDone);
fileChunk = file.slice(chunkStart, chunkEnd);

var TotalLoaded = chunksDone * chunkSize;

var req = new XMLHttpRequest();

req.upload.addEventListener('progress', function (progressobject) {
    var ThisProgress = progressobject.loaded ? progressobject.loaded : 0;
    var OverallPctComplete = parseFloat((TotalLoaded + ThisProgress) / filesize);
    ProgressCallBack({ PercentComplete: OverallPctComplete });
}, false);

req.open("PUT", url, true);
req.setRequestHeader("Content-Range", "bytes " + chunkStart + "-" + (chunkEnd - 1) + "/" + filesize);

req.onload = function (e) {
    var response = JSON.parse(req.response);
    var Status = req.status;
    var CallBackObject = {
        StatusCode: Status,
        ResponseObject: req,
    };

    if (Status == 202) { //response ready for another chunk.
        var range = response.nextExpectedRanges[0].split('-'),
            chunkStart = Number(range[0]),
            nextChunk = chunkStart + chunkSize,
            chunkEnd = nextChunk > filesize ? filesize : nextChunk;

        chunksDone++;
        TotalLoaded = chunksDone * chunkSize;

        CallBackObject.Code = "Accepted",
        CallBackObject.Message = "Upload Another Chunk";

        chunkedUpload(url, file, chunkSize, chunkStart, chunkEnd, chunks, chunksDone++, ProgressCallBack, CompleteCallBack);

    } else {//we are done
        if (Status == 201 || Status == 200) {//successfully created or uploaded
            CallBackObject.Code = 'Success';
            CallBackObject.Message = 'File was Uploaded Successfully.';
        } else { //we had an error.
            var ErrorCode = response.error ? response.error.code : 'Unknown Error Code';
            var ErrorMessage = response.error ? response.error.message : 'Unknown Error Message';

            CallBackObject.Code = ErrorCode;
            CallBackObject.Message = ErrorMessage;
        };
        CompleteCallBack(CallBackObject);
    };


};

req.send(fileChunk);
}