How to copy files between sites using JavaScript REST in Office365 / SharePoint 2013
http://techmikael.blogspot.in/2013/07/how-to-copy-files-between-sites-using.html
I’m currently playing with a POC for an App, and wanted to try to do the App as a SharePoint hosted one, only using JavaScript and REST.
The starting point was to call _vti_bin/ExcelRest.asmx on the host web from my app web, but this end-point does neither support CORS nor JSONP, so it can’t be used directly. My next thought was; Ok, let’s copy the file from the host web over to my app web, then call ExcelRest locally. Easier said than done!
While the final solution seems easy enough, the research, trial and error have taken me about 3 days. I’m now sharing this with you so you can spend your valuable time increasing the international GDP instead.
Note: If you want to copy files between two libraries on the same level, then you can use the copyTo method. http://server/site/_api/web/folders/GetByUrl('/site/srclib')/Files/getbyurl('madcow.xlsx')/copyTo(strNewUrl = '/site/targetlib/madcow.xlsx,bOverWrite = true)
Problem
Copy a file from a document library in one site to a document library in a different site using JavaScript and REST.
The code samples have URL’s using the App web proxy, but it’s easily modifiable for non-app work as well.
Step 1 – Reading the file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var hostweburl = decodeURIComponent(getParameterByName('SPHostUrl'));
var appweburl = decodeURIComponent(getParameterByName('SPAppWebUrl'));
var fileContentUrl = "_api/SP.AppContextSite(@target)/web/GetFileByServerRelativeUrl('/site/library/madcow.xlsx')/$value?@target='" + hostweburl + "'";
var executor = new SP.RequestExecutor(appweburl);
var info = {
url: fileContentUrl,
method: "GET",
binaryStringResponseBody: true,
success: function (data) {
//binary data available in data.body
var result = data.body;
},
error: function (err) {
alert(JSON.stringify(err));
}
};
executor.executeAsync(info);
The important parameter here is setting binaryStringResponseBody to true. Without this parameter the response is being decoded as UTF-8 and the result in the success callback is garbled data, which leads to a corrupt file on save.
The binaryStringResponseBody parameter is not documented anywhere, but I stumbled upon binaryStringRequestbody in an msdn article which was used when uploading a file, and I figured it was worth a shot. Opening SP.RequestExecutor.debug.js I indeed found this parameter.
Step 2 – Patching SP.RequestExecutor.debug.js
Adding binaryStringResponseBody will upon return of the call cause a script error as seen in the figure below.
The method in question is reading over the response byte-by-byte from an Uint8Array, building a correctly encoded string. The issue is that it tries to concatenate to a variable named ret, which is not defined. The defined variable is named $v_0, and here we have a real bug in the script. The bug is there both in Office365 and SharePoint 2013 on-premise.
Luckily for us patching JavaScript is super easy. You merely override the methods involved somewhere in your own code before it’s being called. In the below sample it’s being called once the SP.RequestExecutor.js library has been loaded. The method named BinaryDecode is the one with the error, but you have to override more methods as the originator called is internalProcessXMLHttpRequestOnreadystatechange, and it cascades to calling other internal functions which can be renamed at random as the method names are autogenerated. (This happened for me today and I had to change just overrinding the first function).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
$.getScript(scriptbase + "SP.RequestExecutor.js", function(){
SP.RequestExecutorInternalSharedUtility.BinaryDecode = function SP_RequestExecutorInternalSharedUtility$BinaryDecode(data) {
var ret = '';
if (data) {
var byteArray = new Uint8Array(data);
for (var i = 0; i < data.byteLength; i++) {
ret = ret + String.fromCharCode(byteArray[i]);
}
}
;
return ret;
};
SP.RequestExecutorUtility.IsDefined = function SP_RequestExecutorUtility$$1(data) {
var nullValue = null;
return data === nullValue || typeof data === 'undefined' || !data.length;
};
SP.RequestExecutor.ParseHeaders = function SP_RequestExecutor$ParseHeaders(headers) {
if (SP.RequestExecutorUtility.IsDefined(headers)) {
return null;
}
var result = {};
var reSplit = new RegExp('\r?\n');
var headerArray = headers.split(reSplit);
for (var i = 0; i < headerArray.length; i++) {
var currentHeader = headerArray[i];
if (!SP.RequestExecutorUtility.IsDefined(currentHeader)) {
var splitPos = currentHeader.indexOf(':');
if (splitPos > 0) {
var key = currentHeader.substr(0, splitPos);
var value = currentHeader.substr(splitPos + 1);
key = SP.RequestExecutorNative.trim(key);
value = SP.RequestExecutorNative.trim(value);
result[key.toUpperCase()] = value;
}
}
}
return result;
};
SP.RequestExecutor.internalProcessXMLHttpRequestOnreadystatechange = function SP_RequestExecutor$internalProcessXMLHttpRequestOnreadystatechange(xhr, requestInfo, timeoutId) {
if (xhr.readyState === 4) {
if (timeoutId) {
window.clearTimeout(timeoutId);
}
xhr.onreadystatechange = SP.RequestExecutorNative.emptyCallback;
var responseInfo = new SP.ResponseInfo();
responseInfo.state = requestInfo.state;
responseInfo.responseAvailable = true;
if (requestInfo.binaryStringResponseBody) {
responseInfo.body = SP.RequestExecutorInternalSharedUtility.BinaryDecode(xhr.response);
}
else {
responseInfo.body = xhr.responseText;
}
responseInfo.statusCode = xhr.status;
responseInfo.statusText = xhr.statusText;
responseInfo.contentType = xhr.getResponseHeader('content-type');
responseInfo.allResponseHeaders = xhr.getAllResponseHeaders();
responseInfo.headers = SP.RequestExecutor.ParseHeaders(responseInfo.allResponseHeaders);
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 1223) {
if (requestInfo.success) {
requestInfo.success(responseInfo);
}
}
else {
var error = SP.RequestExecutorErrors.httpError;
var statusText = xhr.statusText;
if (requestInfo.error) {
requestInfo.error(responseInfo, error, statusText);
}
}
}
};
});
Step 3 – Uploading the file
The next step is to save the file in a library on my app web. The crucial part again is to make sure the data is being treated as binary, this time withbinaryStringRequestBody set to true. Make a note of the digest variable as well. On a page inheriting the SP masterpage you can get this value with $("#__REQUESTDIGEST").val(). If not then you have to execute a separate call to _api/contextinfo. The code for that is at the bottom of this post.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var appweburl = decodeURIComponent(getParameterByName('SPAppWebUrl'));
var executor = new SP.RequestExecutor(appweburl);
var info = {
url: "_api/web/GetFolderByServerRelativeUrl('/appWebtargetFolder')/Files/Add(url='madcow.xlsx', overwrite=true)",
method: "POST",
headers: {
"Accept": "application/json; odata=verbose",
"X-RequestDigest": digest
},
contentType: "application/json;odata=verbose",
binaryStringRequestBody: true,
body: arrayBuffer,
success: function(data) {
alert("Success! Your file was uploaded to SharePoint.");
},
error: function (err) {
alert("Oooooops... it looks like something went wrong uploading your file.");
}
};
executor.executeAsync(info);
Journey
I started out using jQuery.ajax for my REST calls, but I did not manage to get the encoding right no matter how many posts I read on this. I read through a lot on the following links which led me to the final solution:
- http://social.msdn.microsoft.com/Forums/sharepoint/en-US/02bfbcdc-73c8-4fa5-8967-cfd903a0d72e/javascript-client-object-model-openbinary-method – which got me started on reading files, but the encoding was wrong
- http://www.shillier.com/archive/2013/03/26/uploading-files-in-sharepoint-2013-using-csom-and-rest.aspx – which has good code and samples on uploading files with CSOM and REST, also mentioning that CSOM has a limit on files being 1.5mb, which is why I went for REST.
- http://msdn.microsoft.com/en-us/library/jj164022.aspx – General REST information
- http://blogs.msdn.com/b/uksharepoint/archive/2013/04/20/uploading-files-using-the-rest-api-and-client-side-techniques.aspx – Sample code for uploading using binaryStringRequestBody
Get the digest value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$.ajax({
url: "_api/contextinfo",
type: "POST",
contentType: "application/x-www-url-encoded",
dataType: "json",
headers: {
"Accept": "application/json; odata=verbose",
},
success: function (data) {
if (data.d) {
var digest = data.d.GetContextWebInformation.FormDigestValue;
}
},
error: function (err) {
alert(JSON.stringify(err));
}
});
How to copy files between sites using JavaScript REST in Office365 / SharePoint 2013的更多相关文章
- 关于在SharePoint 2013(2010)中Javascript如何实现批量批准的自定义操作功能?
1.概述: SharePoint 2013(包括SharePoint 2010)提供了很方便的,多选的界面,但是很多操作还是不能批量进行,比如:批准的功能.如果您要解决方案不关心代码,那么请直接联系作 ...
- Xcode6 ADD Copy Files Build Phase 是灰色的
在学习的怎样写frameWork的时候,查看一个教程How to Create a Framework for iOS [一个中文翻译 创建自己的framework] 其中一个步骤就是添加一个Cop ...
- How do I copy files that need root access with scp
server - How do I copy files that need root access with scp? - Ask Ubuntuhttps://askubuntu.com/quest ...
- Gradle Goodness: Copy Files with Filtering
Gradle Goodness: Copy Files with Filtering Gradle's copy task is very powerful and includes filterin ...
- [MSDN] 使用 SharePoint 2013 中的 JavaScript 库代码完成基本操作
MSDN:http://msdn.microsoft.com/zh-cn/library/jj163201.aspx 了解如何编写代码以在 SharePoint 2013 中使用 JavaScript ...
- SharePoint 2013 中使用 JavaScript Like 和Unlike list item/page/document
SharePoint 2013中新增了很多社交功能,比如用户可以like/unlike 任何一个 list item/page/document,这是一个非常不错的功能. 但有时觉得like/unli ...
- [Forward]Visual Guide: Setting up My Sites in SharePoint 2013
from http://blog.sharedove.com/adisjugo/index.php/2012/07/25/visual-guide-setting-up-my-sites-in-sh ...
- [Bash] Move and Copy Files and Folders with Bash
In this lesson we’ll learn how to move and rename files (mv) and copy (cp) them. Move index.html to ...
- VS Copy Files after build
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <ItemGroup> ...
随机推荐
- 【原】Configuring Oracle Data Guard In Physical Standby Database
作者:david_zhang@sh [转载时请以超链接形式标明文章] http://www.cnblogs.com/david-zhang-index/p/5042640.html参照文档:https ...
- [CS231n-CNN] Training Neural Networks Part 1 : activation functions, weight initialization, gradient flow, batch normalization | babysitting the learning process, hyperparameter optimization
课程主页:http://cs231n.stanford.edu/ Introduction to neural networks -Training Neural Network ________ ...
- 自己动手搭建 CAS(Central Authentication Service) 环境,为了单点登录(Single Sign On , 简称 SSO )
介绍 刚刚搭建 CAS 成功了,现在记录下来,怕以后忘记,同时也给需要帮助的人.CAS 搭建需要服务端和客户端,服务端是 Java 写的,如果自己搭建则需要安装与配置 Java 环境.客户端可以是多种 ...
- 修改efi分区
换了ssd硬盘,速度真快! 我特意准备了一个efi分区(fat32,忘记指定efi了),然而win10安装时不能指定启动位置,自己创建了一块. 下面我们把让win10使用自己创建的efi分区. 以管理 ...
- [作业向]tinyhttp web服务器设计及完整代码
最近看了<HTTP权威指南>和<UNP>有了写一个简单的web服务器的想法,正好这个学期没有什么课,所以就花了一个星期这样写了一个出来,鉴于本人水平有限,如果有什么设计或代码错 ...
- iOS-沙盒路径总结、文件管理NSFileManager总结
// // ViewController.m // 沙盒操作 // // Created by mncong on 15/11/26. // Copyright © 2015年 mancong ...
- #敏捷个人资料# 免费下载 《敏捷个人-认识自我,管理自我 v0.8.pdf》
快乐.高效.平衡,做一个爱学习.有目标.懂生活的影响者. 从今天开始至7月31日,点击http://t.cn/Rv3koHU 免费收到 1000多页的 <敏捷个人-认识自我,管理自我 v0.8. ...
- 第九篇 SQL Server代理了解作业和安全
本篇文章是SQL Server代理系列的第九篇,详细内容请参考原文 在这一系列的上一篇,学习了如何在SQL Server代理作业步骤启动外部程序.你可以使用过时的ActiveX系统,运行批处理命令脚本 ...
- 安装DRBD的一些问题
安装DRBD,建议用源代码包先生成rpm包来安装,不要用直接download的rpm包,有可能会用不了,因为这跟系统内核版本有关系,在2.6.33版本以前内核没有集成drbd, A.先安装一些其它 ...
- 点餐APP 冲刺三总结
一转眼所有的冲刺都完成了,而今次的冲刺主要是完善数据库,而我们 也成功地实现了,虽然过程很艰辛,但是我们每一个人都学习到了很多新 知识,这是最好的收获.因为今学期没有软件工程的课程,所以大家都是 利用 ...