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