JMeter when uploading a file, how do I parameterize content-disposition filename?

Source: Internet
Author: User

The user-defined content-disposition is invalid when the problem description file is uploaded.
When I write the stress test script, there is an upload page, the server is based on the user passed the filename value in the content-disposition to define the file name to save. But it is not possible for testers to prepare a different file for each request (the workload is gone), so the filename in the content-disposition passed to the server by JMeter must be random and not duplicated.
Someone asked, when the user real upload, browser to the server filename is also upload file name? No, JS this modified filename:
            Uploader.onbeforeuploaditem = function (item) {                //Modify name                var timeStamp = new Date (). GetTime ();                var fileName = item.file.name;                Item.file.name = TimeStamp + filename.substr (Filename.lastindexof ('. '));                var day = $filter (' Date ') (new Date (), ' yyyyMMdd ');                Item.url = [Item.url, "Batchimport", Item.importtype, Day, Session.userid].join ("/");            };

I tried a variety of methods, trying to modify the server received the filename value, the result has failed. The author tries to have the following methods:
1. Add an HTTP parameter
, we expect the server to receive a filename value of 00004000.xls instead of 00000000.xls.
The result server received a 00000000.xls. The server returns the storage path to the client as certificate:/batchimport/meradd/20141128/1/00000000.xls. To view this HTTP request, you can see the following information:
POST HTTP://SERVERIP/UPLOAD/BATCHIMPORT/MERADD/20141128/1

POST Data:
--doztx5jroixjtocyspzyj1wvqtoagxmqhhqho4i
Content-disposition:form-data; Name= "Content-disposition"
Content-type:text/plain; Charset=us-ascii
Content-transfer-encoding:8bit

Form-data; Name= "14170058206940.xls"; Filename= "00004000.xls"
--doztx5jroixjtocyspzyj1wvqtoagxmqhhqho4i
Content-disposition:form-data; Name= "File"; Filename= " 00000000.xls"
Content-type:application/vnd.ms-excel
Content-transfer-encoding:binary

<actual file content, not shown here>
--doztx5jroixjtocyspzyj1wvqtoagxmqhhqho4i--

Cookie Data:
$Version = 0; JSESSIONID=AC79777AEFE5AFC690623FCCB09E5DD5; $Path =/

Request Headers:
Connection:keep-alive
content-length:34786
Content-type:multipart/form-data; Boundary=doztx5jroixjtocyspzyj1wvqtoagxmqhhqho4i
Host:serverip
user-agent:apache-httpclient/4.2.6 (Java 1.5)
It seems that JMeter has lost all of our content-disposition parameter names.
2. Add the HTTP header Manager
, we expect the server to receive a filename value of 40004000.xls instead of 00000000.xls.
Then we make a request and then look at this HTTP request and see the following information:
POST HTTP://SERVERIP/UPLOAD/BATCHIMPORT/MERADD/20141128/1

POST Data:
--bnkvcnweqwptjtoyincdn6jjfzjazbe550a-
Content-disposition:form-data; Name= "File"; Filename= " 00000000.xls"
Content-type:application/vnd.ms-excel
Content-transfer-encoding:binary

<actual file content, not shown here>
--BNKVCNWEQWPTJTOYINCDN6JJFZJAZBE550A---

Cookie Data:
$Version = 0; JSESSIONID=49AB53310FB7241B5544B4E747A58F80; $Path =/

Request Headers:
Connection:keep-alive
Content-disposition:form-data; Name= "File"; Filename= "40004000.xls"
content-length:34535
Content-type:multipart/form-data; boundary=bnkvcnweqwptjtoyincdn6jjfzjazbe550a-
Host:serverip
user-agent:apache-httpclient/4.2.6 (Java 1.5)
This time the JMeter did not lose our content-disposition, it appeared in the Request Headers inside. But the server seems to be reading the filename in content-disposition in POST data. The storage path that the server returns to the client is the certificate:/batchimport/meradd/20141128/1/ 00000000.xls
3. Using BeanShell
, we expect the server to receive a filename value of 40004004.xls instead of 00000000.xls. We were expecting to make a request to the server again. The request is as follows:
POST HTTP://SERVERIP/UPLOAD/BATCHIMPORT/MERADD/20141128/1

POST Data:
--lws2euvpppudxcft7ds4rpqqje2up_0lame6qx2q
Content-disposition:form-data; Name= "File"; Filename= " 00000000.xls"
Content-type:application/vnd.ms-excel
Content-transfer-encoding:binary

<actual file content, not shown here>
--lws2euvpppudxcft7ds4rpqqje2up_0lame6qx2q--

Cookie Data:
$Version = 0; JSESSIONID=81514F48024CE0B4CB53DB0CBC283C11; $Path =/

Request Headers:
Connection:keep-alive
content-length:34543
Content-type:multipart/form-data; boundary=lws2euvpppudxcft7ds4rpqqje2up_0lame6qx2q
Host:serverip
user-agent:apache-httpclient/4.2.6 (Java 1.5)
The result is that both the front BeanShell and the BeanShell listener are clearly powerless for our needs.
To the almighty Google for help, the result is basically it's impossible.
Finally, the author with depressed mood to find the responsibility of the project, trying to persuade him, the server should not be sent by the client filename to save the file name, should have their own set of randomly generated file name rules, the answer is: NO.
Under the helpless, the author had to see the source code of JMeter. Well, the source code for JMeter 2.12 (the pure *.java file in the SRC directory) is a full 6.75 MB. And still managed with ANT code, a dense mass of people looking at Sen. Ah, not bad also to see, no way ah, who let us eat performance test this bowl of rice, work is always to continue.
Bite the bullet to look at the afternoon, the result is very unfortunate, found that JMeter is the content-disposition in the filename of the dead, it did not want to leave the user to the filename of the parametric approach!
For example, Org.apache.jmeter.protocol.http.sampler.PostWriter's Writestartfilemultipart method is to write dead:
    /**     * Write The start of a file multipart, up to the point where the     * actual file content should be written     * /    private void Writestartfilemultipart (OutputStream out, string filename,            string NameField, String mimetype)            throws IOException {        write (out, "content-disposition:form-data; Name=\ ""); $NON-nls-1$        Write (out, NameField);        Write (out, "\"; filename=\ ");//$NON-nls-1$        write (out, new File (filename). GetName ());        Writeln (out, "\" "); $NON-nls-1$        Writeln (out, "Content-type:" + mimetype); $NON-nls-1$        Writeln (out, "content-transfer-encoding:binary");//$NON-nls-1$        out.write (CRLF);    }

Although very angry, but feel that finally came to the wrong place, continue to see the source code.
Review the Postwriter Unit test code org.apache.jmeter.protocol.http.sampler.PostWriterTest, which has the following statement in the test Sendpostdata method:
Postwriter.setheaders (connection, sampler);
Postwriter.sendpostdata (connection, sampler);
That is, first write the head, then write the post package body. Org.apache.jmeter.protocol.http.sampler.HTTPJavaImpl's Sample method also confirms this:
                try {conn = setupconnection (URL, method, res);                    Attempt the CONNECTION:SAVEDCONN = conn;                    Conn.connect ();                Break  } catch (Bindexception e) {if (retry >= max_conn_retries) {log.error ("Can ' t                        Connect after "+retry+" retries, "+e";                    Throw e;                    } log.debug ("Bind exception, try again");                        if (conn!=null) {savedconn = null;//We don ' t want interrupt to try disconnection again                    Conn.disconnect ();                    } setusekeepalive (FALSE); Continue                    Try again} catch (IOException e) {log.debug ("Connection failed, giving up");                Throw e; }} if (Retry > Max_conn_retries) {//This should never happen, but ... throw new bindexception (); }//Nice, we ' ve got a connection. Finish sending the Request:if (Method.equals (httpconstants.post)) {String postbody = sendpostd                ATA (conn);            Res.setquerystring (Postbody); }

conn = setupconnection (URL, method, res); When the connection is established, the head is written (take the Setupconnection method below), and the String postbody = sendpostdata (conn) behind it; To start sending packages such as files.
Why does the JMeter set the HTTP information header to work anyway?
Look at Org.apache.jmeter.protocol.http.sampler.HTTPJavaImpl's Setupconnection method:
    Protected HttpURLConnection Setupconnection (URL u, String method, Httpsampleresult res) throws IOException {SS        Lmanager sslmgr = null; if (HTTPConstants.PROTOCOL_HTTPS.equalsIgnoreCase (U.getprotocol ())) {try {sslmgr=sslmanager.ge Tinstance (); N.B. This needs to is done before opening the connection} catch (Exception e) {Log.warn ("Pr            Oblem creating the Sslmanager: ", e);        }} final HttpURLConnection conn;        Final String proxyhost = Getproxyhost ();        Final int proxyport = Getproxyportint (); if (proxyhost.length () > 0 && proxyport > 0) {Proxy proxy = new Proxy (Proxy.Type.HTTP, New Inetso            Cketaddress (ProxyHost, ProxyPort));            Todo-how to define proxy authentication for a single connection?                 It's not clear if the is possible//String user = Getproxyuser ();/if (User.length () > 0) {// AutHenticator auth = new Proxyauthenticator (user, Getproxypass ());/} conn = (httpurlconnection) u.ope        Nconnection (proxy);        } else {conn = (httpurlconnection) u.openconnection (); }//Update follow redirects setting just for this connection conn.setinstancefollowredirects (Getautoredirec        TS ());        CTO of int = Getconnecttimeout ();        if (CTO > 0) {conn.setconnecttimeout (CTO);        } int rto = Getresponsetimeout ();        if (RTO > 0) {conn.setreadtimeout (RTO);  } if (HTTPConstants.PROTOCOL_HTTPS.equalsIgnoreCase (U.getprotocol ())) {try {if (null! =            Sslmgr) {sslmgr.setcontext (conn);//n.b. Must is done after opening connection}            } catch (Exception e) {log.warn ("problem setting the Sslmanager for the connection:", e); }}//A well-bahaved browser is supposed to Send ' connection:close '//with the last request to an HTTP server. Instead, most browsers//leave it to the server to close the connection after their//timeout period.        Leave it to the JMeter user to decide.        if (getusekeepalive ()) {Conn.setrequestproperty (httpconstants.header_connection, httpconstants.keep_alive);        } else {Conn.setrequestproperty (httpconstants.header_connection, httpconstants.connection_close);        } conn.setrequestmethod (method);        Setconnectionheaders (conn, U, Getheadermanager (), Getcachemanager ());        String cookies = Setconnectioncookie (conn, U, Getcookiemanager ());        Setconnectionauthorization (conn, U, Getauthmanager ());        if (Method.equals (httpconstants.post)) {setpostheaders (conn);        } else if (Method.equals (httpconstants.put)) {setputheaders (conn);   } if (res! = null) {res.setrequestheaders (getconnectionheaders (conn));         Res.setcookies (cookies);    } return conn; }

This method establishes an HTTP connection and, before returning the connection, writes the contents of the user's HTTP header manager to the connection (Setconnectionheaders (conn, U, Getheadermanager (), Getcachemanager ()); ), and then call Postwriter's write header method (Setpostheaders (conn)). This also explains the two content-disposition issues on the top of this article.
That means Content-disposition wrote it two times! Unfortunately, HTTP does not have a repetitive check on content-disposition! Even more unfortunate, even if the HTTP will do a repetitive check on content-disposition, our header information manager will not be customized, the above code has been explained, JMeter will first write the header information manager properties, and then call Postwriter to do Content-disposition write, the latter will overwrite the former!
This is simply awful. This should be a bug in JMeter, or a bad place to do it, because it's blocking our custom content-disposition this way.
Solution HTTP Request-Implementation the solution to the Java choice is to do it yourself, clothed. Now that JMeter has blocked the road, we can go and open the road-just adjust the writestartfilemultipart of the Postwriter source code:
    /**     * Write The start of a file multipart, up to the point where the     * actual file content should be written     * /    private void Writestartfilemultipart (OutputStream out, string filename,            string NameField, String mimetype)            throws IOException {        write (out, "content-disposition:form-data; Name=\ ""); $NON-nls-1$        Write (out, NameField);        Write (out, "\"; filename=\ ");//$NON-nls-1$        write (out, NameField);        Writeln (out, "\" "); $NON-nls-1$        Writeln (out, "Content-type:" + mimetype); $NON-nls-1$        Writeln (out, "content-transfer-encoding:binary");//$NON-nls-1$        out.write (CRLF);    }

In fact, only a change, that is, the original write (out, new File (filename). GetName ()); SwitchWrite (out, NameField);
Then the JMeter installation directory under the Lib/ext directory of Apachejmeter_http.jar Decompression, will we modify the compiled Postwriter.class the original flap off, RePack (JAR-CVF apachejmeter_ Http.jar *), the original Apachejmeter_http.jar deleted, using the new packaging.
Above is the workaround for the Sampler JVM default HTTP request (that is, your sampler-HTTP request-Implementation selected Java). If our HTTP request is HttpClient4, or HttpClient3.1 (if you do not select, JMeter default is HttpClient4, the author is using the default, that is, not selected implementation)? The following is a HTTPCLIENT4 solution.

HTTP request-Implementation selected HTTPCLIENT4 solution View Org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl's SENDPOSTDA Ta method,
Find the following sentence:
            viewablefilebody[] filebodies = new Viewablefilebody[files.length];                for (int i=0; i < files.length; i++) {Httpfilearg file = Files[i];                Filebodies[i] = new Viewablefilebody (New File (File.getpath ()), File.getmimetype ());            Multipart.addpart (File.getparamname (), filebodies[i]);            } post.setentity (MultiPart);                if (multipart.isrepeatable ()) {Bytearrayoutputstream bos = new Bytearrayoutputstream ();                for (Viewablefilebody filebody:filebodies) {filebody.hidefiledata = true;                } multipart.writeto (BOS);                for (Viewablefilebody filebody:filebodies) {filebody.hidefiledata = false;                } bos.flush (); We get the posted bytes using the encoding used to create it postedbody.append (new String (Bos.tobytearra Y (), conteNtencoding = = null?                "Us-ascii"//$NON-nls-1$ This was the default used by httpclient:contentencoding)); Bos.close ();

As you can see, the file information is in the Postedbody.append (new String (Bos.tobytearray), the sentence is written to the post body (the reader is interested in the words can go to breakpoint tracking, or log authentication), it writes this The Viewablefilebody object. Find the Viewablefilebody class, the source code is:
    Helper class so we can generate request data without dumping entire file contents    private static Class Viewablefile Body extends Filebody {        private boolean hidefiledata;                Public viewablefilebody (file file, String mimeType) {            super (file, mimeType);            Hidefiledata = false;        }        @Override public        void WriteTo (final outputstream out) throws IOException {            if (hidefiledata) {                Out.write (" <actual file content, not shown here> ". GetBytes ());//encoding does not really matter here            } else {                super. WriteTo (out);}}}    

Can see it inherits from Org.apache.http.entity.mime.content.filebody,filebody has GetFileName method, view its source code:
    Public String GetFileName () {        return this.file.getName ();    }

OK, here's the start. Modify the inner class of the Org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl Viewablefilebody as follows:
    Helper class so we can generate request data without dumping entire file contents    private static Class Viewablefile Body extends Filebody {        private boolean hidefiledata;                Public viewablefilebody (file file, String mimeType) {            super (file, mimeType);            Hidefiledata = false;        }        @Override public        void WriteTo (final outputstream out) throws IOException {            if (hidefiledata) {                Out.write (" <actual file content, not shown here> ". GetBytes ());//encoding does not really matter here            } else {                super. WriteTo (out);            }        }                @Override public        String  GetFileName () {        string filename = This.getfile (). GetName ();        filename = System.currenttimemillis () + filename.substring (Filename.lastindexof ('. '));        return filename;                }    }

OK, compile-overwrite-pack, and then delete the original Apachejmeter_http.jar under Lib/ext under the JMeter installation directory, using the new package. To re-execute the test, intercept the HTTP request as follows:
POST HTTP://SERVERIP/UPLOAD/BATCHIMPORT/MERADD/20141128/1

POST Data:
--qpmvliwpjdoajsqgkd-ux3tr_7hnpx3s1k8ka
Content-disposition:form-data; Name= "File"; Filename= " 1417182984171.xls"
Content-type:application/vnd.ms-excel
Content-transfer-encoding:binary

<actual file content, not shown here>
--qpmvliwpjdoajsqgkd-ux3tr_7hnpx3s1k8ka--

Cookie Data:
$Version = 0; Jsessionid=56e8e454ea4f1378aae45dd0a89a9fe5; $Path =/

Request Headers:
Connection:keep-alive
content-length:34542
Content-type:multipart/form-data; Boundary=qpmvliwpjdoajsqgkd-ux3tr_7hnpx3s1k8ka
Host:serverip
user-agent:apache-httpclient/4.2.6 (Java 1.5)
As you can see, filename is no longer 00000000.xls, and the storage path returned by the server is/batchimport/meradd/20141128/1/ 1417182984171.xls。 It worked.
Note: The version of JMeter Binaries and Source downloaded by the author is 2.12.
HTTP request-Implementation The solution for HttpClient3.1 Yes, yes, that's org.apache.jmeter.protocol.http.sampler.HTTPHC3Impl. This is left to the smart reader to achieve it:)

JMeter when uploading a file, how do I parameterize content-disposition filename?

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.