在Android App中使用multipart / form-data POST发送ZIP-archive

时间:2014-08-25 18:12:17

标签: android post upload multipart form-data

我正在尝试发布ZIP存档。

我有一个网站,可以上传文件。只需使用文件选择器选择文件即可。单击“上传”按钮时,网站会重新加载。在网站重新加载之前,它会检查是否有通过POST提交的文件。因此,如果选择了文件,则会将文件复制到文件夹(包括一些重命名操作)。这是我网站上的整个上传过程,它运行正常。

我想在我的Android应用中使用此服务。我只想提交文件,以便PHP代码可以完成剩下的工作。我关注了一些SO帖子和教程,但在检查了fileZilla后,我遗憾地注意到,目录中没有文件,应该在哪里。

我正在使用AsyncTask。我有三个扩展AsyncTask的类,所以我对此很熟悉。 AsyncTask-Code似乎也运行良好。问题是我在doInBackground方法中的上传代码。

这就是我的代码的样子:

protected String doInBackground(String... sUrl)
{
    HttpURLConnection conn = null;
    DataOutputStream dOut = null;
    DataInputStream dIn = null;

    String filename = path;
    String lineEnd = "\r\n";
    String twoHyphens = "--";
    String boundary = "*****";

    int bytesRead, bytesAvailable, bufferSize;
    byte[] buffer;
    int maxBufferSize = 1*1024*1024;
    String urlString = Constants.URL_UPLOAD;

    try
    {
        // ********************** Client Request

        FileInputStream fileIn = new FileInputStream(new File(filename));


        URL url =  new URL(urlString);

        conn = (HttpURLConnection) url.openConnection();
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setRequestProperty("Content-Type", "multipart/form-data);boundary="+boundary);
        conn.setRequestProperty("Cookie", Constants.COOKIE_KEY + "=" + Constants.cookie);

        dOut = new DataOutputStream(conn.getOutputStream());
        dOut.writeBytes(twoHyphens + boundary + lineEnd);
        dOut.writeBytes("Content-Disposition: form-data; name=\"fileupload\";filename=\"" + path + "\"" + lineEnd);
        dOut.writeBytes(lineEnd);

        bytesAvailable = fileIn.available();
        bufferSize = Math.min(bytesAvailable, maxBufferSize);
        buffer = new byte[bufferSize];
        bytesRead = fileIn.read(buffer, 0, bufferSize);

        while(bytesRead > 0)
        {
            dOut.write(buffer, 0, bufferSize);
            bytesAvailable = fileIn.available();
            bufferSize = Math.min(bytesAvailable, maxBufferSize);
            bytesRead = fileIn.read(buffer, 0, bufferSize);
        }

        dOut.writeBytes(lineEnd);
        dOut.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

        fileIn.close();
        dOut.flush();
        dOut.close();
    }
    catch(MalformedURLException ex)
    {
        Log.e("Debug", "error: " + ex.getMessage(), ex);
    }
    catch(IOException ioex)
    {
        Log.e("Debug", "error: " + ioex.getMessage(), ioex);
    }

    try
    {
        dIn = new DataInputStream(conn.getInputStream());
        String str;

        while((str = dIn.readLine()) != null)
        {
            Log.e("Debug", "Server Response " + str);
            responseData = str;
        }

        dIn.close();
    }
    catch(IOException ioex)
    {
        Log.e("Debug", "error: " + ioex.getMessage(), ioex);
    }
    return null;
}

我想,我的错误就在那一行:

dOut.writeBytes("Content-Disposition: form-data; name=\"fileupload\";filename=\"" + path + "\"" + lineEnd);

我认为该行定义了Http-Header。 首先,我只使用字符串作为其他帖子中使用的“名称”。一些研究告诉我,我必须使用我的表单中该字段的name-tag的值,其中包含我想要发布的值。在我的情况下,这将是文件选择器,其名称为“fileupload”。 (或者我应该采用我的表格名称,即“上传”?) 让我困惑的另一点是“文件名”的值。我应该使用整个路径,例如“path / to / android.app.zip”,还是应该使用文件名:“android.app.zip”?

另外我认为,必须有更多的错误,因为我尝试了名称和文件名的所有组合。

我希望有人发现错误。

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

我找到了解决方案,但它确实有效。 我没有太大变化,但我认为那里有一些小错误,其他帖子中没有提到过。我将在我的代码中评论一些重要的观点。另外,我在服务器上的目录中添加了777权限。不知道它有帮助,但是,正如我所说,在给定的权限和错误修复后我的代码工作。

protected String doInBackground(String... sUrl)
{
    HttpURLConnection conn = null;
    DataOutputStream dOut = null;
    String lineEnd = "\r\n";
    String twoHyphens = "--";
    String boundary = "*****";
    int bytesRead, bytesAvailable, bufferSize;
    byte[] buffer;
    int maxBuffersize = 1*1024*1024;
    File file = new File(path);  

     //path was declared in the constructor of my AsyncTask-Class. 
     // path has to look like this: /storage/some/dirs/myarchive.zip, when you use a .zip
    //At this point it is clear, that you have to use the full path, but many posts just used
    // a simple name like "file" as an example in the following code, which confused me.

    try
    {
        FileInputStream fileIn = new FileInputStream(file);
        URL url =  new URL(Constants.URL_UPLOAD);

        conn = (HttpURLConnection) url.openConnection();
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        conn.setRequestProperty("Cookie", Constants.COOKIE_KEY + "=" + Constants.cookie);

    //There wasn't even one tutorial or post, which uses cookies. That was a problem, too.
    //When you have a serious website, not just an example, then you possibly use cookies.
    //My constant "COOKIE_KEY" describes the cookie key (or name) you defined in your php code
    //and .cookie is just the value. For example it could look like this "user_cookie=MrFoo", when the cookie contains the username of a "MrFoo"

        conn.setRequestMethod("POST");
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setRequestProperty("ENCTYPE", "multipart/form-data");
        conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
        conn.setRequestProperty("fileupload", path);

        dOut = new DataOutputStream(conn.getOutputStream());
        dOut.writeBytes(twoHyphens + boundary + lineEnd);
        dOut.writeBytes("Content-Disposition: form-data; name=\"fileupload\";filename=\"" + path + "\"" + lineEnd);

    //Here you have to use the field in your html/php-code, which contains the file after "name"
    //In my case it was a file chooser (input type="file"), which has the name="fileupload". 
    //There wasn't any post again, where it was explained what kind of value is expected here.


        dOut.writeBytes(lineEnd);

        bytesAvailable = fileIn.available();
        bufferSize = Math.min(bytesAvailable, maxBuffersize);
        buffer = new byte[bufferSize];
        bytesRead = fileIn.read(buffer, 0, bufferSize);

        while(bytesRead > 0)
        {
            dOut.write(buffer, 0, bufferSize);
            bytesAvailable = fileIn.available();
            bufferSize = Math.min(bytesAvailable, maxBuffersize);
            bytesRead = fileIn.read(buffer, 0, bufferSize);
        }

        dOut.writeBytes(lineEnd);
        dOut.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

        responseCode = conn.getResponseCode();
        String responseMessage = conn.getResponseMessage();

        Log.i("UPLOAD", "HTTP Response is: " + responseCode + ": " + responseMessage);

        if(responseCode == 200)
        {
            return null;
        }

        fileIn.close();
        dOut.flush();
        dOut.close();
    }
        catch(MalformedURLException e)
        {
            return e.toString();
        }
    catch(Exception e)
    {
        return e.toString();
    }

    return responseCode + "";
}

如果有人想查看其他代码,例如onPostExecute,请参阅此处:

private Context context;
    private ProgressDialog dialog;
    private String path;
    private PowerManager.WakeLock wakeLock;
    private int responseCod    

public UploadTask(Context ctx, ProgressDialog dialog, String path)
    {
        context = ctx;
        this.dialog = dialog;
        this.path = path;
        responseCode = 0;

    }



    @Override
    protected void onPreExecute()
    {
        super.onPreExecute();

        //locks CPU to prevend shut down during download

        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
        wakeLock.acquire();
        dialog.show();
    }

    @Override
    protected void onProgressUpdate(Integer... progress)
    {
        super.onProgressUpdate(progress);

        dialog.setIndeterminate(false);
        dialog.setMax(100);
        dialog.setProgress(progress[0]);
    }

    protected void onPostExecute(String result)
    {
        wakeLock.release();
        dialog.dismiss();

        if(result != null)
            Toast.makeText(context, "Upload error: " + result, Toast.LENGTH_LONG).show();
        else
            Toast.makeText(context, "File uploaded!!", Toast.LENGTH_SHORT).show();
    }

只有一个进度对话框和其他内容。

我希望我可以帮助任何人。