⬆️ ⬇️

cross-domain UPLOAD files with progress bar, without reloading the page, and without using flash

Draste all.

Thank you for deciding to read this post.



The task was as follows, due to the fact that the well-known file loader FancyUploader is not always, but quite often buggy, especially if you use a proxy, you had to develop a system that showed progress bar when downloading a file ... There is a lot of such systems , but all either use other flash loaders or other Api, which in this case is a negative result of development.



After some time of searching, a system based on the apc module was found habrahabr.ru/blogs/webdev/17620 . Everything would be fine, but to hang an additional cache module on a server where the technology cache was already used was meaningless. Then I came across information about the NGINX upload progress module (http://wiki.nginx.org/NginxHttpUploadProgressModule) and it really works ... Especially since our servers installed exactly nginx as a front-end server.

')

But the most interesting was ahead.



Usually files are downloaded to the place where the site itself is (for the same domain), but this is not the case with our company. All pictures and videos are on a separate server, and accordingly it’s good if the upload were right on it, so as not to download the site channel.



After configuring nginx with the upload progress module and using the wiki.nginx.org/NginxHttpUploadProgressModule example, the progress bar for loading the file has started.



To transfer the file to the necessary server, not a lot was changed, and naturally nginx itself must be located on h_t_t_p: //STORAGE-SERVER.com.



< code>

function openProgressBar () {

/ * generate random progress-id * /

uuid = "";

for (i = 0; i <32; i ++) {

uuid + = Math.floor (Math.random () * 16) .toString (16);

}

/ * patch the progress-id tag

document.getElementById ("upload"). action = " h_t_t_p: //STORAGE-SERVER.com /upload.php?X-Progress-ID=" + uuid;



/ * call the progress-updater every 1000ms * /

interval = window.setInterval (function () {fetch (uuid);}, 1000);

}



For feedback and reading the transmitted volume, use the query

req = new XMLHttpRequest();

req.open("GET", "/progress", 1);



and problem number 1, the request should be on h_t_t_p: //STORAGE-SERVER.com, and not on the server on which the site itself is located, but as it is known cross-domain ajax requests are prohibited by the security policy. The solution turned out to be quite simple and consisted in redirecting the request from the current domain to the needed one with help of several lines in the config file nginx which is located on the sending side.

location ^~ /progress {

# proxy to upstream server

proxy_pass h_t_t_p://STORAGE-SERVER.com;

proxy_redirect default;

}



Hooray ... Everything turned out ... The case remained for the small, get info about where the file was actually recorded on h_t_t_p: //STORAGE-SERVER.com, with what name. Those. processing h_t_t_p: //STORAGE-SERVER.com/upload.php should send the result of the recording, for example in the following format {'filepath': '/ user_upload / photo / 2/232/23456 / 23456.jpg', 'filetype': ' image ',' error ':' 0 '}. The format of the data that is returned after processing can of course be different, any given by you. I have such as indicated.



The result after execution in the source code will look like

<iframe id="uploadframe" name="uploadframe" width="0" height="0" frameborder="0" border="0" src="about:blank">

<html>

<head>

</head>

<body>

{'filepath':'/user_upload/photo/2/232/23456/23456.jpg', 'filetype':'image', 'error':'0'}

</body>

</html>

</iframe>



Those. after the transfer of the last byte, the action form is executed in the specified frame.



And as it turned out, it is impossible to remove it from there in the usual ways, because it would be a big security hole. You can get data only from a frame that is stored in the same domain space with the document itself, otherwise it will be possible to get some data from otherdomain.com .



The method of scientific and non-scientific spear was tried by masses of ways to get around this, but the only and as it turned out very simple was the use of anchors in the site link. But for cross-browser compatibility, you need to use not parent.location.hash, but simply parent.location (otherwise it will work only in FireFox.)



For this, the handler file does not just form a line with the result, but a script is formed after which we get

<iframe id="uploadframe" name="uploadframe" width="0" height="0" frameborder="0" border="0" src="about:blank">

<html>

<head>



<script>

parent.location="h_t_t_p://mysite.com/uploadfile/index.php#{'filepath':'/user_upload/photo/2/232/23456/23456.jpg', 'filetype':'image', 'error':'0'}";

</script>



</head>

<body>

</body>

</html>

</iframe>



After that, it remains to consider the anchor and process it

function fetch(uuid) {

req = new XMLHttpRequest();

req.open("GET", "/progress", 1);

req.setRequestHeader("X-Progress-ID", uuid);

req.onreadystatechange = function () {

if (req.readyState == 4) {

if (req.status == 200) {

/* poor-man JSON parser */

var upload = eval(req.responseText);



document.getElementById('tp').innerHTML = upload.state;



/* change the width if the inner progress-bar */

if (upload.state == 'uploading') {

bar = document.getElementById('progressbar');

w = 400 * upload.received / upload.size;

bar.style.width = w + 'px';

}else if (upload.state == 'done') {

// , ,

//

window.clearTimeout(interval);



bar = document.getElementById('progressbar');

bar.style.width = '401px';



//************** *********************************

var result = window.location.hash;

location.hash='progress'; //

result = result.slice(1); // '#'

//

//javascript ( )

//**********************************************************************

}

/* we are done, stop the interval */

if (upload.state == 'done' || upload.state == 'error') {

window.clearTimeout(interval);

}

}

}

}

req.send(null);

}



and of course the form itself for selecting the download file



<h2> </h2><br/>

<form id="upload" enctype="multipart/form-data"

action="/upload.php" target="uploadframe" method="post"

onsubmit="openProgressBar(); return true;">

<input type="hidden" name="MAX_FILE_SIZE" value="300000000" />

<input name="Filedata" type="file" label="fileupload" />

<input type="submit" value="" />

</form>

<iframe id="uploadframe" name="uploadframe" width="0" height="0" frameborder="0" border="0" src="about:blank"></iframe>



<div id="progress" style="width: 400px; border: 1px solid black; height:10px; display: none;">

<div id="progressbar"

style="width: 0px; background-color: black; margin-left:-1px; border: 0px solid black; height:10px;">

</div>

</div>



<div id="tp">(progress)</div>





That's all gentlemen ... In the end, it turned out to be a good and quite universal system (especially if the processor sent the current url) to upload files directly to the storage server with the download status on the site (which is not on the storage server).



The advantages of such a system are NO FLASH or other applets, the versatility of the solution, and most importantly, cross-domain file upload without load on the site (upload to storage directly)



Shortcomings - does not work in IE6, there is no possibility of simultaneous loading of several files (as in FancyUploader3)



Thanks for attention.

Source: https://habr.com/ru/post/79342/



All Articles