アップロードされたファイルをサーブレットアプリケーションに保存するための推奨される方法


121

私が読んで、ここには、トランザクションの移植ではなく、外部パラメータを必要とするように、1つは、とにかく、サーバーにファイルを保存してはならないこと。ただし、tomcat(7)のtmpソリューションが必要であり、知りたいサーバーマシンを(相対的に)制御しているとすると、

  • ファイルを保存するのに最適な場所はどこですか?保存する必要があります/WEB-INF/uploadsここでアドバイス)またはその下$CATALINA_BASEここを参照)または... JavaEE 6チュートリアルは、ユーザー(:wtf :) からパスを取得します。注意:ファイルは決してダウンロード可能であってはなりません。

  • ここに詳述されているように構成パラメータを設定する必要がありますか?私はいくつかのコードに感謝します(むしろ相対パスを指定します-したがって、少なくともTomcatポータブルです)- Part.write()有望に見えますが、どうやら絶対パスが必要です

  • このアプローチの短所とデータベース/ JCRリポジトリの短所の説明に興味があります。

残念ながら、 @ BalusCによるFileServletはファイルのダウンロードに集中していますが、ファイルのアップロードに関する彼の回答は、ファイルの保存場所に関する部分をスキップしています。

DBまたはJCR実装(jackrabbitのような)を使用するように簡単に変換できるソリューションが望ましいでしょう。


それを行う私の最後の方法については、以下の回答を参照し
Mr_and_Mrs_D

回答:


165

ページの更新後にのみ利用可能なアップロードされたイメージへの回答に記載されている理由により、IDEのプロジェクトフォルダーまたはサーバーのデプロイフォルダー以外のアクセス可能な場所に保存します

  1. IDEのプロジェクトフォルダの変更は、サーバーの作業フォルダにすぐには反映されません。IDEには一種のバックグラウンドジョブがあり、サーバーの作業フォルダーが最後の更新と同期されます(これはIDE用語では "発行"と呼ばれます)。これが、発生している問題の主な原因です。

  2. 実際のコードでは、アップロードされたファイルをwebappのデプロイフォルダーに保存してもまったく機能しない場合があります。一部のサーバーは(デフォルトまたは設定により)デプロイされたWARファイルをローカルディスクファイルシステムに展開せず、代わりに完全にメモリに展開します。基本的に、デプロイされたWARファイルを編集して再デプロイしないと、メモリに新しいファイルを作成できません。

  3. サーバーがデプロイされたWARファイルをローカルディスクファイルシステムに展開しても、新しく作成されたすべてのファイルは、元のWARファイルの一部ではないため、再デプロイまたは単純な再起動で失われます。

methodを使用ないgetRealPath()限り、ローカルディスクファイルシステムのどこに保存されるかは、私や他の人には関係ありません。その方法を使用すると、いずれにしても憂慮すべきことです。

保存場所へのパスは、さまざまな方法で定義できます。あなたはそれをすべて自分でしなければなりません。おそらくこれは、サーバーがすべてを自動的に実行することを何らかの形で期待していたため、混乱が生じている場所です。は最終的なアップロード先を指定して@MultipartConfig(location)いませが、ケースファイルサイズの一時的な保存場所がメモリストレージのしきい値を超えていることに注意してください。

したがって、最終的な保管場所へのパスは、次のいずれかの方法で定義できます。

  • ハードコード:

      File uploads = new File("/path/to/uploads");
  • を介した環境変数SET UPLOAD_LOCATION=/path/to/uploads

      File uploads = new File(System.getenv("UPLOAD_LOCATION"));
  • サーバー起動時のVM引数-Dupload.location="/path/to/uploads"

      File uploads = new File(System.getProperty("upload.location"));
  • *.propertiesファイルエントリupload.location=/path/to/uploads

      File uploads = new File(properties.getProperty("upload.location"));
  • web.xml <context-param>名前upload.locationと値/path/to/uploads

      File uploads = new File(getServletContext().getInitParameter("upload.location"));
  • 存在する場合は、サーバー提供の場所を使用します(例:JBoss AS / WildFly)

      File uploads = new File(System.getProperty("jboss.server.data.dir"), "uploads");

どちらの方法でも、次のようにファイルを簡単に参照して保存できます。

File file = new File(uploads, "somefilename.ext");

try (InputStream input = part.getInputStream()) {
    Files.copy(input, file.toPath());
}

または、一意のファイル名を自動生成して、ユーザーが既存のファイルを誤って同じ名前で上書きしないようにする場合:

File file = File.createTempFile("somefilename-", ".ext", uploads);

try (InputStream input = part.getInputStream()) {
    Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}

partJSP /サーブレットで取得する方法は、JSP /サーブレットを使用してサーバーにファイルをアップロードする方法で答えられていますか?partJSFで取得する方法は、JSF 2.2 <h:inputFile>を使用してファイルをアップロードする方法で答えられていますか?保存されたファイルはどこにありますか?

注:で定義された一時的な保存場所に対する相対パスを解釈するため、使用しないでください。Part#write()@MultipartConfig(location)

以下も参照してください。


@MultipartConfig(location)指定の一時ファイルサイズがメモリストレージのしきい値を超えた場合に、サーバが使用するstorgeの場所は、あなたが最終的にそれたいと思いません恒久的な保管場所が格納されます。この値のデフォルトは、java.io.tmpdirシステムプロパティによって識別されるパスです。失敗したJSFの試みにも、この関連の回答を参照してください。stackoverflow.com/questions/18478154/...
BalusC

1
おかげで-希望は私が馬鹿鳴りませんが、からのこの引用はPart.write>> これは、例えば、使用する特定の実装を可能にするファイルのリネーム、可能であれば、むしろので、パフォーマンスの大幅な利益を得て、基礎となるデータのすべてをコピーするよりも、いくつかと一緒にいくつかのapache libから不明な「カット」(vsコピー)メソッドを使用すると、自分でバイトを書き込む手間が省けます。また、すでにそこにファイルを再作成する(ここ参照
Mr_and_Mrs_D

はい。サーブレット3.0をすでに使用している場合は、を利用できますPart#write()。それで答えを更新しました。
BalusC 2013

投稿を更新していただきありがとうございます-Tomcatのようなプロパティはあり"jboss.server.data.dir"ますか?
Mr_and_Mrs_D 2013

1
いいえ、ありません。
BalusC 2013

7

受け入れられた回答に基づいて、最終的な方法を投稿します。

@SuppressWarnings("serial")
@WebServlet("/")
@MultipartConfig
public final class DataCollectionServlet extends Controller {

    private static final String UPLOAD_LOCATION_PROPERTY_KEY="upload.location";
    private String uploadsDirName;

    @Override
    public void init() throws ServletException {
        super.init();
        uploadsDirName = property(UPLOAD_LOCATION_PROPERTY_KEY);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // ...
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        Collection<Part> parts = req.getParts();
        for (Part part : parts) {
            File save = new File(uploadsDirName, getFilename(part) + "_"
                + System.currentTimeMillis());
            final String absolutePath = save.getAbsolutePath();
            log.debug(absolutePath);
            part.write(absolutePath);
            sc.getRequestDispatcher(DATA_COLLECTION_JSP).forward(req, resp);
        }
    }

    // helpers
    private static String getFilename(Part part) {
        // courtesy of BalusC : http://stackoverflow.com/a/2424824/281545
        for (String cd : part.getHeader("content-disposition").split(";")) {
            if (cd.trim().startsWith("filename")) {
                String filename = cd.substring(cd.indexOf('=') + 1).trim()
                        .replace("\"", "");
                return filename.substring(filename.lastIndexOf('/') + 1)
                        .substring(filename.lastIndexOf('\\') + 1); // MSIE fix.
            }
        }
        return null;
    }
}

どこ :

@SuppressWarnings("serial")
class Controller extends HttpServlet {

    static final String DATA_COLLECTION_JSP="/WEB-INF/jsp/data_collection.jsp";
    static ServletContext sc;
    Logger log;
    // private
    // "/WEB-INF/app.properties" also works...
    private static final String PROPERTIES_PATH = "WEB-INF/app.properties";
    private Properties properties;

    @Override
    public void init() throws ServletException {
        super.init();
        // synchronize !
        if (sc == null) sc = getServletContext();
        log = LoggerFactory.getLogger(this.getClass());
        try {
            loadProperties();
        } catch (IOException e) {
            throw new RuntimeException("Can't load properties file", e);
        }
    }

    private void loadProperties() throws IOException {
        try(InputStream is= sc.getResourceAsStream(PROPERTIES_PATH)) {
                if (is == null)
                    throw new RuntimeException("Can't locate properties file");
                properties = new Properties();
                properties.load(is);
        }
    }

    String property(final String key) {
        return properties.getProperty(key);
    }
}

および/WEB-INF/app.properties:

upload.location=C:/_/

HTHおよびバグを見つけた場合はお知らせください


1
SOに依存しないソリューションが必要な場合、それは両方の(win / ux)ケースで機能しますか?別のupload.locationパスを設定する必要がありますか、それとも他にヒントがありますか?
pikimota
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.