web-dev-qa-db-ja.com

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

私は here を読みます。ファイルは移植性がなく、トランザクションに対応しておらず、外部パラメーターを必要とするため、サーバーにファイルを保存しないでください。ただし、Tomcat(7)のtmpソリューションが必要であり、知りたいサーバーマシンを(相対)制御できることを考えると:

  • ファイルを保存するのに最適な場所は何ですか? /WEB-INF/uploadshere に対して推奨)または$CATALINA_BASEhere を参照)または...の下に保存する必要がありますか? JavaEE 6チュートリアル ユーザーからパスを取得 (:wtf :)。 NB:ファイルはいかなる手段でもダウンロード可能ではありません。

  • 詳細な設定パラメータを設定する必要があります here ?私はいくつかのコードを感謝します(相対パスを与えたい-それは少なくともTomcatポータブルです)- Part.write() は有望に見えますが、明らかに絶対パスが必要です

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

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

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

112
Mr_and_Mrs_D

Uploaded image only availableの回答に記載されている理由により、IDEのプロジェクトフォルダーまたはサーバーのデプロイフォルダーのアクセス可能な場所exceptの任意の場所に保存します。ページを更新した後

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

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

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

あなたが donotである限り、ローカルディスクファイルシステム上で正確に保存される私や他の人にとっては、それは本当に重要ではありません。 getRealPath() method を使用してください。その方法の使用は、anyの場合の警告です。

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

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

  • ハードコーディング:

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

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

    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);
}

JSP/Servletでpartを取得する方法は で回答されています。JSP/ Servletを使用してサーバーにファイルをアップロードするには? およびJSFでpartを取得する方法は JSF 2.2 <h:inputFile>を使用してファイルをアップロードするには?保存されたファイルはどこですか?

注:notは、 Part#write() を使用します。これは、@MultipartConfig(location)で定義された一時保管場所に関連するパスを解釈します。

こちらもご覧ください:

149
BalusC

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

@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およびバグを見つけたら私に知らせて下さい

6
Mr_and_Mrs_D